From 9a143ef43528415976ae6c52e0980cbcc87af003 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Tue, 3 Mar 2015 00:18:52 -0800 Subject: [PATCH 01/49] start building the schema for both the posts and comments --- models/Comment.js | 20 ++++++++++++++++++++ models/Post.js | 23 +++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 models/Comment.js create mode 100644 models/Post.js diff --git a/models/Comment.js b/models/Comment.js new file mode 100644 index 0000000000..f53e6a55a0 --- /dev/null +++ b/models/Comment.js @@ -0,0 +1,20 @@ +var mongoose = require('mongoose'); +var secrets = require('../config/secrets'); + +var commentSchema = new mongoose.Schema({ + rank: Number, + upVotes: [ + { + upVotedBy: ObjectId, + upVotedTime: Number + } + ], + author: { + username: String, + id: ObjectId, + picture: String + } +}); + +module.exports = mongoose.model('BonfireCompletion', bonfireCompletionSchema); + diff --git a/models/Post.js b/models/Post.js new file mode 100644 index 0000000000..2a34aed306 --- /dev/null +++ b/models/Post.js @@ -0,0 +1,23 @@ +var mongoose = require('mongoose'); +var secrets = require('../config/secrets'); + +var postSchema = new mongoose.Schema({ + headline: String, + link: String, + body: String, + rank: Number, + upVotes: [ + { + upVotedBy: ObjectId, + upVotedTime: Number + } + ], + author: { + username: String, + id: ObjectId, + picture: String + } +}); + +module.exports = mongoose.model('BonfireCompletion', bonfireCompletionSchema); + From b7a95ee56f7b05cb2a232c0ac632cb8297f7b7d2 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Tue, 3 Mar 2015 01:41:32 -0800 Subject: [PATCH 02/49] passing off hacker news clone to Nathan and his pair --- controllers/story.js | 20 +++++++++++++ models/Comment.js | 19 ++++-------- models/Post.js | 23 --------------- models/Story.js | 15 ++++++++++ seed_data/seed.js | 22 +++++++++++++- seed_data/stories.json | 67 ++++++++++++++++++++++++++++++++++++++++++ views/post/index.jade | 5 ++++ views/post/posts.jade | 24 +++++++++++++++ 8 files changed, 157 insertions(+), 38 deletions(-) create mode 100644 controllers/story.js delete mode 100644 models/Post.js create mode 100644 models/Story.js create mode 100644 seed_data/stories.json create mode 100644 views/post/index.jade create mode 100644 views/post/posts.jade diff --git a/controllers/story.js b/controllers/story.js new file mode 100644 index 0000000000..688e8a8590 --- /dev/null +++ b/controllers/story.js @@ -0,0 +1,20 @@ +var R = require('ramda'), + debug = require('debug')('freecc:cntr:post'), + Post = require('./../models/Post'), + Comment = require('./../models/Comment'), + User = require('./../models/User'), + resources = require('./resources'); + +/** + * Post Controller + */ + +exports.index = function(req, res, next) { + var posts = Post.find({}).sort({'rank': -1}); + posts.exec(function(err, listings) { + if (err) { + throw err; + } + res.json(listing); + }); +}; \ No newline at end of file diff --git a/models/Comment.js b/models/Comment.js index f53e6a55a0..1a3a1639ce 100644 --- a/models/Comment.js +++ b/models/Comment.js @@ -2,19 +2,10 @@ var mongoose = require('mongoose'); var secrets = require('../config/secrets'); var commentSchema = new mongoose.Schema({ - rank: Number, - upVotes: [ - { - upVotedBy: ObjectId, - upVotedTime: Number - } - ], - author: { - username: String, - id: ObjectId, - picture: String - } + rank: { type: Number, default: -Infinity }, + upVotes: { type: Array, default: [] }, + author: {}, + comments: { type: Array, default: [] } }); -module.exports = mongoose.model('BonfireCompletion', bonfireCompletionSchema); - +module.exports = mongoose.model('Comment', commentSchema); \ No newline at end of file diff --git a/models/Post.js b/models/Post.js deleted file mode 100644 index 2a34aed306..0000000000 --- a/models/Post.js +++ /dev/null @@ -1,23 +0,0 @@ -var mongoose = require('mongoose'); -var secrets = require('../config/secrets'); - -var postSchema = new mongoose.Schema({ - headline: String, - link: String, - body: String, - rank: Number, - upVotes: [ - { - upVotedBy: ObjectId, - upVotedTime: Number - } - ], - author: { - username: String, - id: ObjectId, - picture: String - } -}); - -module.exports = mongoose.model('BonfireCompletion', bonfireCompletionSchema); - diff --git a/models/Story.js b/models/Story.js new file mode 100644 index 0000000000..251cefa42d --- /dev/null +++ b/models/Story.js @@ -0,0 +1,15 @@ +var mongoose = require('mongoose'); +var secrets = require('../config/secrets'); + +var storySchema = new mongoose.Schema({ + headline: String, + link: String, + body: String, + rank: { type: Number, default: -Infinity }, + upVotes: { type: Array, default: [] }, + author: {}, + comments: { type: Array, default: [] } +}); + +module.exports = mongoose.model('Story', storySchema); + diff --git a/seed_data/seed.js b/seed_data/seed.js index 6549c3cc71..b59c381645 100644 --- a/seed_data/seed.js +++ b/seed_data/seed.js @@ -1,17 +1,20 @@ require('dotenv').load(); var Challenge = require('../models/Challenge.js'), Bonfire = require('../models/Bonfire.js'), + Comment = require('../models/Comment.js'), + Story = require('../models/Story.js'), Courseware = require('../models/Courseware.js'), mongoose = require('mongoose'), secrets = require('../config/secrets'), challenges = require('./challenges.json'), coursewares = require('./coursewares.json'), + stories = require('./stories.json'), bonfires = require('./bonfires.json'); mongoose.connect(secrets.db); var counter = 0; -var offerings = 3; +var offerings = 4; var CompletionMonitor = function() { counter++; @@ -73,4 +76,21 @@ Courseware.remove({}, function(err, data) { CompletionMonitor(); }); console.log('coursewares'); +}); + +Story.remove({}, function(err, data) { + if (err) { + console.error(err); + } else { + console.log('Deleted ', data); + } + Story.create(coursewares, function(err, data) { + if (err) { + console.log(err); + } else { + console.log('Saved ', data); + } + CompletionMonitor(); + }); + console.log('stories'); }); \ No newline at end of file diff --git a/seed_data/stories.json b/seed_data/stories.json new file mode 100644 index 0000000000..c53742c0ff --- /dev/null +++ b/seed_data/stories.json @@ -0,0 +1,67 @@ +[ + { + "headline": "Cat sits on keyboard 0", + "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", + "body": "cats love keyboards you know", + "rank": 0, + "upVotes": [], + "author": { + "username": "terakilobyte", + "id": "a2ad135e2aa27c14fc73ee11", + "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" + }, + "comments": [] + }, + { + "headline": "Cat sits on keyboard 1", + "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", + "body": "cats love keyboards you know", + "rank": 1, + "upVotes": [], + "author": { + "username": "terakilobyte", + "id": "a2ad135e2aa27c14fc73ee11", + "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" + }, + "comments": [] + }, + { + "headline": "Cat sits on keyboard 3", + "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", + "body": "cats love keyboards you know", + "rank": 3, + "upVotes": [], + "author": { + "username": "terakilobyte", + "id": "a2ad135e2aa27c14fc73ee11", + "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" + }, + "comments": [] + }, + { + "headline": "Cat sits on keyboard 20", + "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", + "body": "cats love keyboards you know", + "rank": 20, + "upVotes": [], + "author": { + "username": "terakilobyte", + "id": "a2ad135e2aa27c14fc73ee11", + "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" + }, + "comments": [] + }, + { + "headline": "Cat sits on keyboard 2", + "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", + "body": "cats love keyboards you know", + "rank": 2, + "upVotes": [], + "author": { + "username": "terakilobyte", + "id": "a2ad135e2aa27c14fc73ee11", + "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" + }, + "comments": [] + } +] \ No newline at end of file diff --git a/views/post/index.jade b/views/post/index.jade new file mode 100644 index 0000000000..bdf5e171b8 --- /dev/null +++ b/views/post/index.jade @@ -0,0 +1,5 @@ +extends ../layout +block content + .panel.text-center + h1.hug-top Camper News + include parials/post \ No newline at end of file diff --git a/views/post/posts.jade b/views/post/posts.jade new file mode 100644 index 0000000000..f20fd43aab --- /dev/null +++ b/views/post/posts.jade @@ -0,0 +1,24 @@ +h3 + ol#bonfireList Test! + + //script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") + //script. + // var getLinkedName = function getLinkedName(name) { + // return name.toLowerCase().replace(/\s/g, '-'); + // } + // $.ajax({ + // url: '/bonfires/getBonfireList', + // type: 'GET' + // }) + // .success( + // function(data) { + // for (var i = 0; i < data.bonfireList.length; i++) { + // var li = document.createElement('li'); + // var linkedName = getLinkedName(data.bonfireList[i].name); + // if (R.contains(data.bonfireList[i].id, data.completedList)) { + // $(li).addClass('strikethrough'); + // } + // $(li).html("" + data.bonfireList[i].name + ""); + // $(li).appendTo($('#bonfireList')); + // } + // }); \ No newline at end of file From 52e494407bd0bd39a2b8681b081d1675aaf174bd Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Tue, 3 Mar 2015 19:23:56 +0900 Subject: [PATCH 03/49] Get initial view working, sort out mongo mess with seed file --- app.js | 17 ++++++++++++++++ controllers/story.js | 22 +++++++++++++++------ models/Story.js | 30 ++++++++++++++++++++++------ seed_data/seed.js | 2 +- seed_data/stories.json | 10 +++++----- views/post/index.jade | 4 ++-- views/post/posts.jade | 44 ++++++++++++++++++++---------------------- 7 files changed, 86 insertions(+), 43 deletions(-) diff --git a/app.js b/app.js index 74c920dcec..21b596fcab 100644 --- a/app.js +++ b/app.js @@ -34,6 +34,11 @@ var express = require('express'), bonfireController = require('./controllers/bonfire'), coursewareController = require('./controllers/courseware'), + /** + * Stories + */ + storyController = require('./controllers/story'); + /** * User model */ @@ -272,6 +277,18 @@ app.post( userController.updateProgress ); +/** + * Main routes. + */ +app.get( + '/stories/', + storyController.index +); +app.get( + '/stories/index', + storyController.json +); + /** * Challenge related routes */ diff --git a/controllers/story.js b/controllers/story.js index 688e8a8590..79c9ffd113 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -1,6 +1,6 @@ var R = require('ramda'), debug = require('debug')('freecc:cntr:post'), - Post = require('./../models/Post'), + Story = require('./../models/Story'), Comment = require('./../models/Comment'), User = require('./../models/User'), resources = require('./resources'); @@ -9,12 +9,22 @@ var R = require('ramda'), * Post Controller */ -exports.index = function(req, res, next) { - var posts = Post.find({}).sort({'rank': -1}); - posts.exec(function(err, listings) { +exports.json = function(req, res, next) { + var story = Story.find({}).sort({'rank': -1}); + story.exec(function(err, stories) { if (err) { throw err; } - res.json(listing); + res.json(stories); }); -}; \ No newline at end of file +}; + +exports.index = function(req, res, next) { + var story = Story.find({}).sort({'rank': -1}); + story.exec(function(err, stories) { + if (err) { + throw err; + } + res.render('post/index'); + }); +}; diff --git a/models/Story.js b/models/Story.js index 251cefa42d..ba000b0a7d 100644 --- a/models/Story.js +++ b/models/Story.js @@ -2,13 +2,31 @@ var mongoose = require('mongoose'); var secrets = require('../config/secrets'); var storySchema = new mongoose.Schema({ - headline: String, - link: String, - body: String, - rank: { type: Number, default: -Infinity }, - upVotes: { type: Array, default: [] }, + headline: { + type: String, + unique: false + }, + link: { + type: String, + unique: false + }, + body: { + type: String, + unique: false + }, + rank: { + type: Number, + default: -Infinity + }, + upVotes: { + type: Array, + default: [] + }, author: {}, - comments: { type: Array, default: [] } + comments: { + type: Array, + default: [] + } }); module.exports = mongoose.model('Story', storySchema); diff --git a/seed_data/seed.js b/seed_data/seed.js index b59c381645..447df778f9 100644 --- a/seed_data/seed.js +++ b/seed_data/seed.js @@ -84,7 +84,7 @@ Story.remove({}, function(err, data) { } else { console.log('Deleted ', data); } - Story.create(coursewares, function(err, data) { + Story.create(stories, function(err, data) { if (err) { console.log(err); } else { diff --git a/seed_data/stories.json b/seed_data/stories.json index c53742c0ff..a38fedcdb6 100644 --- a/seed_data/stories.json +++ b/seed_data/stories.json @@ -7,7 +7,7 @@ "upVotes": [], "author": { "username": "terakilobyte", - "id": "a2ad135e2aa27c14fc73ee11", + "userId": "a2ad135e2aa27c14fc73ee11", "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" }, "comments": [] @@ -20,7 +20,7 @@ "upVotes": [], "author": { "username": "terakilobyte", - "id": "a2ad135e2aa27c14fc73ee11", + "userId": "a2ad135e2aa27c14fc73ee22", "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" }, "comments": [] @@ -33,7 +33,7 @@ "upVotes": [], "author": { "username": "terakilobyte", - "id": "a2ad135e2aa27c14fc73ee11", + "userId": "a2ad135e2aa27c14fc73ee33", "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" }, "comments": [] @@ -46,7 +46,7 @@ "upVotes": [], "author": { "username": "terakilobyte", - "id": "a2ad135e2aa27c14fc73ee11", + "userId": "a2ad135e2aa27c14fc73ee44", "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" }, "comments": [] @@ -59,7 +59,7 @@ "upVotes": [], "author": { "username": "terakilobyte", - "id": "a2ad135e2aa27c14fc73ee11", + "userId": "a2ad135e2aa27c14fc73ee55", "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" }, "comments": [] diff --git a/views/post/index.jade b/views/post/index.jade index bdf5e171b8..71172b1d83 100644 --- a/views/post/index.jade +++ b/views/post/index.jade @@ -1,5 +1,5 @@ extends ../layout block content .panel.text-center - h1.hug-top Camper News - include parials/post \ No newline at end of file + h1 Camper News + include ./posts \ No newline at end of file diff --git a/views/post/posts.jade b/views/post/posts.jade index f20fd43aab..ffe7d0e438 100644 --- a/views/post/posts.jade +++ b/views/post/posts.jade @@ -1,24 +1,22 @@ -h3 - ol#bonfireList Test! +h2 + ul#story-list Top Stories + h3 + script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") + script. + var getLinkedName = function getLinkedName(name) { + return name.toLowerCase().replace(/\s/g, '-'); + } + $.ajax({ + url: '/stories/index', + type: 'GET' + }) + .success( + function(data) { + for (var i = 0; i < data.length; i++) { + var li = document.createElement('li'); + var linkedName = getLinkedName(data[i].headline); - //script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") - //script. - // var getLinkedName = function getLinkedName(name) { - // return name.toLowerCase().replace(/\s/g, '-'); - // } - // $.ajax({ - // url: '/bonfires/getBonfireList', - // type: 'GET' - // }) - // .success( - // function(data) { - // for (var i = 0; i < data.bonfireList.length; i++) { - // var li = document.createElement('li'); - // var linkedName = getLinkedName(data.bonfireList[i].name); - // if (R.contains(data.bonfireList[i].id, data.completedList)) { - // $(li).addClass('strikethrough'); - // } - // $(li).html("" + data.bonfireList[i].name + ""); - // $(li).appendTo($('#bonfireList')); - // } - // }); \ No newline at end of file + $(li).html("" + data[i].headline + ""); + $(li).appendTo($('#story-list')); + } + }); \ No newline at end of file From 94bbb7a4a5681130dffcbdeaadcdadc02966f5fc Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Tue, 3 Mar 2015 19:50:16 +0900 Subject: [PATCH 04/49] Beginnings of the show individual post view --- app.js | 4 ++++ controllers/story.js | 37 +++++++++++++++++++++++++++++++++++++ views/post/show.jade | 17 +++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 views/post/show.jade diff --git a/app.js b/app.js index 21b596fcab..a4365992f8 100644 --- a/app.js +++ b/app.js @@ -288,6 +288,10 @@ app.get( '/stories/index', storyController.json ); +app.get( + '/stories/:storyName', + storyController.returnIndividualStory +); /** * Challenge related routes diff --git a/controllers/story.js b/controllers/story.js index 79c9ffd113..e454a91ea2 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -28,3 +28,40 @@ exports.index = function(req, res, next) { res.render('post/index'); }); }; + +exports.returnIndividualStory = function(req, res, next) { + var dashedName = req.params.storyName; + + storyName = dashedName.replace(/\-/g, ' '); + + Story.find({'headline' : new RegExp(storyName, 'i')}, function(err, story) { + if (err) { + next(err); + } + + + if (story.length < 1) { + req.flash('errors', { + msg: "404: We couldn't find a story with that name. Please double check the name." + }); + + return res.redirect('/stories/'); + } + + story = story.pop(); + var dashedNameFull = story.headline.toLowerCase().replace(/\s/g, '-'); + if (dashedNameFull != dashedName) { + return res.redirect('../stories/' + dashedNameFull); + } + + res.render('post/show', { + title: story.headline, + dashedName: story.link, + author: story.author, + body: story.body, + rank: story.rank, + upVotes: story.upVotes, + comments: story.comments + }); + }); +}; diff --git a/views/post/show.jade b/views/post/show.jade new file mode 100644 index 0000000000..f8fdb58a61 --- /dev/null +++ b/views/post/show.jade @@ -0,0 +1,17 @@ +extends ../layout +block content + h1= title + h3= body + h5= rank + + + + +// + title: story.headline, + dashedName: story.link, + author: story.author, + body: story.body, + rank: story.rank, + upVotes: story.upVotes, + comments: story.comments \ No newline at end of file From 4b84b49d07c7fceaaecd33b8996376bf48d8efe2 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Tue, 3 Mar 2015 22:03:33 +0900 Subject: [PATCH 05/49] Added start of individual post view and post lists view, wired upvoting functionality for individual posts --- .eslintrc | 220 ++++++++++++++++++++++++++++++++++++++++++ .gitignore | 2 + app.js | 4 + controllers/story.js | 19 +++- public/css/main.less | 4 + public/js/main.js | 21 ++++ views/post/posts.jade | 43 +++++---- views/post/show.jade | 20 +++- 8 files changed, 307 insertions(+), 26 deletions(-) create mode 100644 .eslintrc diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000000..a167df14d6 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,220 @@ +{ + "ecmaFeatures": { + "jsx": true + }, + "env": { + "browser": true, + "mocha": true, + "node": true + }, + "globals": { + "window": true, + "$": true, + "ga": true, + "jQuery": true + }, + "rules": { + "no-comma-dangle": 2, + "no-cond-assign": 2, + "no-console": 0, + "no-constant-condition": 2, + "no-control-regex": 2, + "no-debugger": 2, + "no-dupe-keys": 2, + "no-empty": 2, + "no-empty-class": 2, + "no-ex-assign": 2, + "no-extra-boolean-cast": 2, + "no-extra-parens": 0, + "no-extra-semi": 2, + "no-func-assign": 2, + "no-inner-declarations": 2, + "no-invalid-regexp": 2, + "no-irregular-whitespace": 2, + "no-negated-in-lhs": 2, + "no-obj-calls": 2, + "no-regex-spaces": 2, + "no-reserved-keys": 0, + "no-sparse-arrays": 2, + "no-unreachable": 2, + "use-isnan": 2, + "valid-jsdoc": 2, + "valid-typeof": 2, + + "block-scoped-var": 2, + "complexity": 0, + "consistent-return": 2, + "curly": 2, + "default-case": 1, + "dot-notation": 0, + "eqeqeq": 1, + "guard-for-in": 1, + "no-alert": 1, + "no-caller": 2, + "no-div-regex": 2, + "no-else-return": 0, + "no-empty-label": 2, + "no-eq-null": 1, + "no-eval": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-fallthrough": 2, + "no-floating-decimal": 2, + "no-implied-eval": 2, + "no-iterator": 2, + "no-labels": 2, + "no-lone-blocks": 2, + "no-loop-func": 1, + "no-multi-spaces": 1, + "no-multi-str": 2, + "no-native-reassign": 2, + "no-new": 2, + "no-new-func": 2, + "no-new-wrappers": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-process-env": 0, + "no-proto": 2, + "no-redeclare": 1, + "no-return-assign": 2, + "no-script-url": 2, + "no-self-compare": 2, + "no-sequences": 2, + "no-unused-expressions": 2, + "no-void": 1, + "no-warning-comments": [ + 1, + { + "terms": [ + "fixme" + ], + "location": "start" + } + ], + "no-with": 2, + "radix": 2, + "vars-on-top": 0, + "wrap-iife": [2, "any"], + "yoda": 0, + + "strict": 0, + + "no-catch-shadow": 2, + "no-delete-var": 2, + "no-label-var": 2, + "no-shadow": 0, + "no-shadow-restricted-names": 2, + "no-undef": 2, + "no-undef-init": 2, + "no-undefined": 1, + "no-unused-vars": 2, + "no-use-before-define": 0, + + "handle-callback-err": 2, + "no-mixed-requires": 0, + "no-new-require": 2, + "no-path-concat": 2, + "no-process-exit": 2, + "no-restricted-modules": 0, + "no-sync": 0, + + "brace-style": [ + 2, + "1tbs", + { "allowSingleLine": true } + ], + "camelcase": 1, + "comma-spacing": [ + 2, + { + "before": false, + "after": true + } + ], + "comma-style": [ + 2, "last" + ], + "consistent-this": 0, + "eol-last": 2, + "func-names": 0, + "func-style": 0, + "key-spacing": [ + 2, + { + "beforeColon": false, + "afterColon": true + } + ], + "max-nested-callbacks": 0, + "new-cap": 0, + "new-parens": 2, + "no-array-constructor": 2, + "no-inline-comments": 1, + "no-lonely-if": 1, + "no-mixed-spaces-and-tabs": 2, + "no-multiple-empty-lines": [ + 1, + { "max": 2 } + ], + "no-nested-ternary": 2, + "no-new-object": 2, + "no-space-before-semi": 2, + "no-spaced-func": 2, + "no-ternary": 0, + "no-trailing-spaces": 1, + "no-underscore-dangle": 0, + "no-wrap-func": 2, + "one-var": 0, + "operator-assignment": 0, + "padded-blocks": 0, + "quote-props": 0, + "quotes": [ + 2, + "single", + "avoid-escape" + ], + "semi": [ + 2, + "always" + ], + "sort-vars": 0, + "space-after-keywords": [ + 2, + "always", + { "checkFunctionKeyword": false } + ], + "space-after-function-names": "never", + "space-before-blocks": [ + 2, + "always" + ], + "space-in-brackets": 0, + "space-in-parens": 0, + "space-infix-ops": 2, + "space-return-throw-case": 2, + "space-unary-ops": [ + 1, + { + "words": true, + "nonwords": false + } + ], + "spaced-line-comment": [ + 2, + "always", + { "exceptions": ["-"] } + ], + "wrap-regex": 1, + + "max-depth": 0, + "max-len": [ + 1, + 80, + 2 + ], + "max-params": 0, + "max-statements": 0, + "no-bitwise": 1, + "no-plusplus": 0 + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 002acaf376..01824e83b9 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ node_modules .DS_Store Thumbs.db bower_components +.eslintignore +.eslintrc diff --git a/app.js b/app.js index a4365992f8..a5c3f8e4bf 100644 --- a/app.js +++ b/app.js @@ -292,6 +292,10 @@ app.get( '/stories/:storyName', storyController.returnIndividualStory ); +app.post( + '/stories/upvote/:id', + storyController.upvote +); /** * Challenge related routes diff --git a/controllers/story.js b/controllers/story.js index e454a91ea2..f4cf67ed5d 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -50,9 +50,10 @@ exports.returnIndividualStory = function(req, res, next) { story = story.pop(); var dashedNameFull = story.headline.toLowerCase().replace(/\s/g, '-'); - if (dashedNameFull != dashedName) { + if (dashedNameFull !== dashedName) { return res.redirect('../stories/' + dashedNameFull); } + debug('Story id is', story._id); res.render('post/show', { title: story.headline, @@ -61,7 +62,21 @@ exports.returnIndividualStory = function(req, res, next) { body: story.body, rank: story.rank, upVotes: story.upVotes, - comments: story.comments + comments: story.comments, + id: story._id }); }); }; + +exports.upvote = function(req, res, next) { + var data = req.params.id; + Story.find({'_id': data}, function(err, story) { + if (err) { + throw err; + } + story = story.pop(); + story.rank++; + story.save(); + return res.send(story); + }); +}; diff --git a/public/css/main.less b/public/css/main.less index 9885b31d57..b6099f52c6 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -744,6 +744,10 @@ iframe.iphone { margin-bottom: -4px; } +.story-list li { + padding-bottom: 5px; +} + //uncomment this to see the dimensions of all elements outlined in red //* { // border-color: red; diff --git a/public/js/main.js b/public/js/main.js index aa5b28a913..1613a8cfd4 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -120,6 +120,27 @@ $(document).ready(function() { $('#brief-instructions').show(); $('#long-instructions').hide(); }); + + var upvoteHandler = function () { + var _id = storyId; + + $.ajax({ + type: 'POST', + url: '/stories/upvote/' + _id, + beforeSend: function() { + $('#upvote').unbind('click'); + }, + error: function(xhr, textStatus, errorThrown) { + console.log('got error'); + $('#upvote').bind('click', upvoteHandler); + }, + success: function (data, textStatus, xhr) { + console.log(data); + $('#storyRank').text(data.rank); + } + }); + }; + $('#upvote').on('click', upvoteHandler); }); var profileValidation = angular.module('profileValidation',['ui.bootstrap']); diff --git a/views/post/posts.jade b/views/post/posts.jade index ffe7d0e438..067f8321ae 100644 --- a/views/post/posts.jade +++ b/views/post/posts.jade @@ -1,22 +1,23 @@ -h2 - ul#story-list Top Stories - h3 - script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") - script. - var getLinkedName = function getLinkedName(name) { - return name.toLowerCase().replace(/\s/g, '-'); - } - $.ajax({ - url: '/stories/index', - type: 'GET' - }) - .success( - function(data) { - for (var i = 0; i < data.length; i++) { - var li = document.createElement('li'); - var linkedName = getLinkedName(data[i].headline); +h3 + ul#story-list.story-list - $(li).html("" + data[i].headline + ""); - $(li).appendTo($('#story-list')); - } - }); \ No newline at end of file + script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") + script. + var getLinkedName = function getLinkedName(name) { + return name.toLowerCase().replace(/\s/g, '-'); + } + $.ajax({ + url: '/stories/index', + type: 'GET' + }) + .success( + function(data) { + for (var i = 0; i < data.length; i++) { + var li = document.createElement('li'); + var linkedName = getLinkedName(data[i].headline); + var rank = data[i].rank; + + $(li).html("
" + rank + "
" + data[i].author.username + "
"); + $(li).appendTo($('#story-list')); + } + }); \ No newline at end of file diff --git a/views/post/show.jade b/views/post/show.jade index f8fdb58a61..5e190f0909 100644 --- a/views/post/show.jade +++ b/views/post/show.jade @@ -1,8 +1,22 @@ extends ../layout block content - h1= title - h3= body - h5= rank + script. + var challengeName = 'Story'; + var storyId = !{JSON.stringify(id)}; + .jumbotron + .row + .col-xs-2(style='position: relative; top: 50%; -webkit-transform: translateY(50%); -ms-transform: translateY(50%);transform: translateY(50%);') + h3#storyRank= rank + .col-xs-10.text-center + h1= title + .row + .col-xs-2 + h3 + a#upvote + i.ion-arrow-up-b + .col-xs-10.text-center + h3= body + From 968832ae52beccf66d6ba4332c33323c9245a341 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Tue, 3 Mar 2015 22:52:45 +0900 Subject: [PATCH 06/49] start of comments view --- models/Comment.js | 19 ++++++++++++++++--- views/post/comments.jade | 25 +++++++++++++++++++++++++ views/post/show.jade | 7 +++++++ 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 views/post/comments.jade diff --git a/models/Comment.js b/models/Comment.js index 1a3a1639ce..9f71c32591 100644 --- a/models/Comment.js +++ b/models/Comment.js @@ -2,10 +2,23 @@ var mongoose = require('mongoose'); var secrets = require('../config/secrets'); var commentSchema = new mongoose.Schema({ - rank: { type: Number, default: -Infinity }, - upVotes: { type: Array, default: [] }, + associatedPost: { + type: String, + required: true + }, + rank: { + type: Number, + default: -Infinity + }, + upVotes: { + type: Array, + default: [] + }, author: {}, - comments: { type: Array, default: [] } + comments: { + type: Array, + default: [] + } }); module.exports = mongoose.model('Comment', commentSchema); \ No newline at end of file diff --git a/views/post/comments.jade b/views/post/comments.jade new file mode 100644 index 0000000000..e3f19430ed --- /dev/null +++ b/views/post/comments.jade @@ -0,0 +1,25 @@ +h6 + ul#comment-list.comment-list + + script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") + h1 Super tired folks, going to call it a night + // + script. + var getLinkedName = function getLinkedName(name) { + return name.toLowerCase().replace(/\s/g, '-'); + } + $.ajax({ + url: '/stories/index', + type: 'GET' + }) + .success( + function(data) { + for (var i = 0; i < data.length; i++) { + var li = document.createElement('li'); + var linkedName = getLinkedName(data[i].headline); + var rank = data[i].rank; + + $(li).html("
" + rank + "
" + data[i].author.username + "
"); + $(li).appendTo($('#story-list')); + } + }); \ No newline at end of file diff --git a/views/post/show.jade b/views/post/show.jade index 5e190f0909..698701115c 100644 --- a/views/post/show.jade +++ b/views/post/show.jade @@ -16,6 +16,13 @@ block content i.ion-arrow-up-b .col-xs-10.text-center h3= body + .row.negative-35 + h3.col-xs-12.col-md-6.col-lg-5.text-center Comments + .row + .col-xs-12.col-md-6.col-lg-5 + textarea#comment-box.form-control(name="comment-box", rows=5) + .row + include ./comments From 6ac780e836609e7af4811a477d15e4093a9124ed Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Wed, 4 Mar 2015 07:15:00 +0900 Subject: [PATCH 07/49] Rewire upvote to use post and req.body instead of req.params, add logic on click handler to reject upvotes if the user has already upvoted, coment display now works --- app.js | 8 ++++++- controllers/story.js | 26 +++++++++++++++++---- models/Comment.js | 6 ++++- public/js/main.js | 39 ++++++++++++++++++-------------- seed_data/comments.json | 14 ++++++++++++ seed_data/seed.js | 22 ++++++++++++++++-- seed_data/stories.json | 4 ++-- views/post/comments.jade | 49 ++++++++++++++++++++++++---------------- views/post/index.jade | 2 ++ views/post/show.jade | 21 ++++++++++++----- 10 files changed, 138 insertions(+), 53 deletions(-) create mode 100644 seed_data/comments.json diff --git a/app.js b/app.js index a5c3f8e4bf..bbff557451 100644 --- a/app.js +++ b/app.js @@ -288,12 +288,18 @@ app.get( '/stories/index', storyController.json ); + +app.get( + '/stories/comments/:id', + storyController.comments +); + app.get( '/stories/:storyName', storyController.returnIndividualStory ); app.post( - '/stories/upvote/:id', + '/stories/upvote/', storyController.upvote ); diff --git a/controllers/story.js b/controllers/story.js index f4cf67ed5d..e32a81aba0 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -53,7 +53,7 @@ exports.returnIndividualStory = function(req, res, next) { if (dashedNameFull !== dashedName) { return res.redirect('../stories/' + dashedNameFull); } - debug('Story id is', story._id); + debug('Story', story); res.render('post/show', { title: story.headline, @@ -63,20 +63,38 @@ exports.returnIndividualStory = function(req, res, next) { rank: story.rank, upVotes: story.upVotes, comments: story.comments, - id: story._id + id: story._id, + user: req.user }); }); }; exports.upvote = function(req, res, next) { - var data = req.params.id; - Story.find({'_id': data}, function(err, story) { + var data = req.body.data; + Story.find({'_id': data.id}, function(err, story) { if (err) { throw err; } story = story.pop(); story.rank++; + story.upVotes.push( + { + upVotedBy: data.upVoter._id, + upVotedByUsername: data.upVoter.profile.username + } + ); story.save(); return res.send(story); }); }; + +exports.comments = function(req, res, next) { + var data = req.params.id; + Comment.find({'_id': data}, function(err, comment) { + if (err) { + throw err; + } + comment = comment.pop(); + return res.send(comment); + }); +}; diff --git a/models/Comment.js b/models/Comment.js index 9f71c32591..9206b6ed75 100644 --- a/models/Comment.js +++ b/models/Comment.js @@ -6,11 +6,15 @@ var commentSchema = new mongoose.Schema({ type: String, required: true }, + body: { + type: String, + default: '' + }, rank: { type: Number, default: -Infinity }, - upVotes: { + upvotes: { type: Array, default: [] }, diff --git a/public/js/main.js b/public/js/main.js index 1613a8cfd4..8f7c26ba39 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -107,8 +107,6 @@ $(document).ready(function() { window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1); }); - - // Bonfire instructions functions $('#more-info').on('click', function() { ga('send', 'event', 'Challenge', 'more-info', challengeName); @@ -123,22 +121,29 @@ $(document).ready(function() { var upvoteHandler = function () { var _id = storyId; - - $.ajax({ - type: 'POST', - url: '/stories/upvote/' + _id, - beforeSend: function() { - $('#upvote').unbind('click'); - }, - error: function(xhr, textStatus, errorThrown) { - console.log('got error'); - $('#upvote').bind('click', upvoteHandler); - }, - success: function (data, textStatus, xhr) { - console.log(data); - $('#storyRank').text(data.rank); + $('#upvote').unbind('click'); + var alreadyUpvoted = false; + for (var i = 0; i < upVotes.length; i++) { + if (upVotes[i].upVotedBy === user._id) { + alreadyUpvoted = true; + break; } - }); + } + if (!alreadyUpvoted) { + $.post('/stories/upvote', + { + data: { + id: _id, + upVoter: user + } + }) + .fail(function (xhr, textStatus, errorThrown) { + $('#upvote').bind('click', upvoteHandler); + }) + .done(function (data, textStatus, xhr) { + $('#storyRank').text(data.rank); + }); + } }; $('#upvote').on('click', upvoteHandler); }); diff --git a/seed_data/comments.json b/seed_data/comments.json new file mode 100644 index 0000000000..5f4b1efc37 --- /dev/null +++ b/seed_data/comments.json @@ -0,0 +1,14 @@ +[ + { + "associatedPost": "54f584db16cd6570aa176eda", + "body": "cats like to sit around and be lazy as hell", + "rank": "1", + "upvotes": [], + "author": { + "username" : "terakilonoob", + "picture": "https://thedrinkingtraveler.files.wordpress.com/2015/02/24127-funny-grumpy-cat-memesvery-bad-morning-meme-0rlh4r5c-wallpaper-1024x1024.jpg", + "id" : "ade671aa0756dc61bd485f45" + }, + "comments": [] + } +] \ No newline at end of file diff --git a/seed_data/seed.js b/seed_data/seed.js index 447df778f9..cbf537efa9 100644 --- a/seed_data/seed.js +++ b/seed_data/seed.js @@ -9,12 +9,13 @@ var Challenge = require('../models/Challenge.js'), challenges = require('./challenges.json'), coursewares = require('./coursewares.json'), stories = require('./stories.json'), - bonfires = require('./bonfires.json'); + bonfires = require('./bonfires.json'), + comments = require('./comments.json'); mongoose.connect(secrets.db); var counter = 0; -var offerings = 4; +var offerings = 5; var CompletionMonitor = function() { counter++; @@ -93,4 +94,21 @@ Story.remove({}, function(err, data) { CompletionMonitor(); }); console.log('stories'); +}); + +Comment.remove({}, function(err, data) { + if (err) { + console.error(err); + } else { + console.log('Deleted ', data); + } + Comment.create(comments, function(err, data) { + if (err) { + console.log(err); + } else { + console.log('Saved ', data); + } + CompletionMonitor(); + }); + console.log('stories'); }); \ No newline at end of file diff --git a/seed_data/stories.json b/seed_data/stories.json index a38fedcdb6..fd1a3a1c4f 100644 --- a/seed_data/stories.json +++ b/seed_data/stories.json @@ -41,7 +41,7 @@ { "headline": "Cat sits on keyboard 20", "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", - "body": "cats love keyboards you know", + "body": "ipsizzle dolizzle sit amet, ghetto adipiscing elit. Nullam fo shizzle velizzle, aliquet volutpizzle, suscipizzle shiz, gravida vizzle, arcu. Pellentesque izzle tortor. Sizzle doggy. Boom shackalack izzle dolizzle dapibizzle ass tempizzle tellivizzle. Ma nizzle we gonna chung shiz izzle tellivizzle. Vestibulum dizzle tortor. Pellentesque pimpin' rhoncus you son of a bizzle. In dizzle habitasse platea dictumst. Donec dapibizzle. Curabitizzle pot yippiyo, pretizzle da bomb, mattis izzle, da bomb vitae, nunc. Ass suscipizzle. Cool sempizzle bow wow wow sed purus.", "rank": 20, "upVotes": [], "author": { @@ -49,7 +49,7 @@ "userId": "a2ad135e2aa27c14fc73ee44", "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" }, - "comments": [] + "comments": ["54f61b0e43f0c2b90f162ec4"] }, { "headline": "Cat sits on keyboard 2", diff --git a/views/post/comments.jade b/views/post/comments.jade index e3f19430ed..0efc3dbdac 100644 --- a/views/post/comments.jade +++ b/views/post/comments.jade @@ -2,24 +2,33 @@ h6 ul#comment-list.comment-list script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") - h1 Super tired folks, going to call it a night - // - script. - var getLinkedName = function getLinkedName(name) { - return name.toLowerCase().replace(/\s/g, '-'); - } - $.ajax({ - url: '/stories/index', - type: 'GET' - }) - .success( - function(data) { - for (var i = 0; i < data.length; i++) { - var li = document.createElement('li'); - var linkedName = getLinkedName(data[i].headline); - var rank = data[i].rank; - $(li).html("
" + rank + "
" + data[i].author.username + "
"); - $(li).appendTo($('#story-list')); - } - }); \ No newline at end of file + script. + var commentDetails; + R.forEach(function displayComments(comment) { + console.log('this is the comment id', comment); + $.ajax({ + type: 'GET', + url: '/stories/comments/' + comment, + error: function(xhr, textStatus, errorThrown) { + console.log('got error'); + commentDetails = { + error: true, + body: 'There seems to be a problem fetching this comment.' + } + }, + success: function (data, textStatus, xhr) { + console.log(data); + commentDetails = data; + var li = document.createElement('li'); + $(li) + .html("
" + commentDetails.body + + "
" + commentDetails.rank + "
" + + commentDetails.author.username + "
") + .appendTo($('#comment-list')); + } + + + }) + + }, comments); \ No newline at end of file diff --git a/views/post/index.jade b/views/post/index.jade index 71172b1d83..19a0c21822 100644 --- a/views/post/index.jade +++ b/views/post/index.jade @@ -1,5 +1,7 @@ extends ../layout block content + script. + var challengeName = 'Camper News'; .panel.text-center h1 Camper News include ./posts \ No newline at end of file diff --git a/views/post/show.jade b/views/post/show.jade index 698701115c..3ce14c3c13 100644 --- a/views/post/show.jade +++ b/views/post/show.jade @@ -1,19 +1,23 @@ extends ../layout block content script. - var challengeName = 'Story'; + var challengeName = 'Camper News'; var storyId = !{JSON.stringify(id)}; + var comments = !{JSON.stringify(comments)}; + var upVotes = !{JSON.stringify(upVotes)}; + .jumbotron .row .col-xs-2(style='position: relative; top: 50%; -webkit-transform: translateY(50%); -ms-transform: translateY(50%);transform: translateY(50%);') - h3#storyRank= rank + h3 + a#upvote + i.ion-arrow-up-b .col-xs-10.text-center h1= title .row .col-xs-2 - h3 - a#upvote - i.ion-arrow-up-b + h3#storyRank= rank + .col-xs-10.text-center h3= body .row.negative-35 @@ -21,8 +25,13 @@ block content .row .col-xs-12.col-md-6.col-lg-5 textarea#comment-box.form-control(name="comment-box", rows=5) + if (user) + script. + var user = !{JSON.stringify(user)}; + $('#upvote').unbind('click'); .row - include ./comments + .col-xs-12.col-md-7.col-lg-4.text-left + include ./comments From 260c25a482187702effca2768d4cc6243d538ca6 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Tue, 3 Mar 2015 14:35:35 -0800 Subject: [PATCH 08/49] some minor UI improvements on news --- public/css/main.less | 5 +++++ views/post/index.jade | 5 +++-- views/post/posts.jade | 19 ++++++++++++++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/public/css/main.less b/public/css/main.less index b6099f52c6..d22fc4ea05 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -748,6 +748,11 @@ iframe.iphone { padding-bottom: 5px; } +.big-ion-up-arrow { + font-size: 45px; + margin-top: -10px; +} + //uncomment this to see the dimensions of all elements outlined in red //* { // border-color: red; diff --git a/views/post/index.jade b/views/post/index.jade index 19a0c21822..6f4656f1d2 100644 --- a/views/post/index.jade +++ b/views/post/index.jade @@ -2,6 +2,7 @@ extends ../layout block content script. var challengeName = 'Camper News'; - .panel.text-center - h1 Camper News + .panel.panel-info + .panel-heading.text-center Camper News + .panel-body.hug-top include ./posts \ No newline at end of file diff --git a/views/post/posts.jade b/views/post/posts.jade index 067f8321ae..de2f23ba8f 100644 --- a/views/post/posts.jade +++ b/views/post/posts.jade @@ -17,7 +17,24 @@ h3 var linkedName = getLinkedName(data[i].headline); var rank = data[i].rank; - $(li).html("
" + rank + "
" + data[i].author.username + "
"); + $(li).html("
" + + "
" + + "" + + "
" + + "" + + "
" + + "
" + + "
" + + "
" + + "
" + rank + "
" + + "
" + data[i].author.username + "
" + + "
" + + "
" + + ""); $(li).appendTo($('#story-list')); } }); \ No newline at end of file From 2c375e8c618ffaef2fd77b8cb7e21d489791885e Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Wed, 4 Mar 2015 07:55:13 +0900 Subject: [PATCH 09/49] Improve layout of posts --- views/post/posts.jade | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/views/post/posts.jade b/views/post/posts.jade index de2f23ba8f..84ddbedb23 100644 --- a/views/post/posts.jade +++ b/views/post/posts.jade @@ -17,23 +17,20 @@ h3 var linkedName = getLinkedName(data[i].headline); var rank = data[i].rank; - $(li).html("
" + - "
" + + $(li).html("
" + + "
" + "" + "
" + - "
" + + "" + - "
" + - "
" + "
" + - "
" + - "
" + rank + "
" + - "
" + data[i].author.username + "
" + - "
" + - "
" + + "
" + + "
" + rank + "
" + + "
" + data[i].author.username + "
" + + "
" + ""); $(li).appendTo($('#story-list')); } From d8b8373b7036d552c0f7b43480624b6f77fbdd24 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Tue, 3 Mar 2015 19:55:04 -0800 Subject: [PATCH 10/49] do some refactoring of the camper news views --- controllers/story.js | 16 +++--- models/Story.js | 4 ++ public/css/main.less | 9 +++- public/js/application.js | 2 +- seed_data/stories.json | 49 ++++++++++-------- views/partials/navbar.jade | 2 +- views/post/posts.jade | 37 -------------- views/post/show.jade | 47 ----------------- views/{post => stories}/comments.jade | 0 views/{post => stories}/index.jade | 2 +- views/stories/show.jade | 74 +++++++++++++++++++++++++++ views/stories/stories.jade | 52 +++++++++++++++++++ 12 files changed, 176 insertions(+), 118 deletions(-) delete mode 100644 views/post/posts.jade delete mode 100644 views/post/show.jade rename views/{post => stories}/comments.jade (100%) rename views/{post => stories}/index.jade (86%) create mode 100644 views/stories/show.jade create mode 100644 views/stories/stories.jade diff --git a/controllers/story.js b/controllers/story.js index e32a81aba0..36ae133107 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -1,14 +1,11 @@ var R = require('ramda'), - debug = require('debug')('freecc:cntr:post'), + debug = require('debug')('freecc:cntr:story'), Story = require('./../models/Story'), Comment = require('./../models/Comment'), User = require('./../models/User'), + moment = require('../public/js/lib/moment/moment.js'), resources = require('./resources'); -/** - * Post Controller - */ - exports.json = function(req, res, next) { var story = Story.find({}).sort({'rank': -1}); story.exec(function(err, stories) { @@ -25,7 +22,7 @@ exports.index = function(req, res, next) { if (err) { throw err; } - res.render('post/index'); + res.render('stories/index'); }); }; @@ -55,16 +52,17 @@ exports.returnIndividualStory = function(req, res, next) { } debug('Story', story); - res.render('post/show', { + res.render('stories/show', { title: story.headline, - dashedName: story.link, + link: story.link, author: story.author, body: story.body, rank: story.rank, upVotes: story.upVotes, comments: story.comments, id: story._id, - user: req.user + user: req.user, + timeAgo: moment(story.timePosted).fromNow() }); }); }; diff --git a/models/Story.js b/models/Story.js index ba000b0a7d..79e41d774e 100644 --- a/models/Story.js +++ b/models/Story.js @@ -6,6 +6,10 @@ var storySchema = new mongoose.Schema({ type: String, unique: false }, + timePosted: { + type: Number, + default: 0 + }, link: { type: String, unique: false diff --git a/public/css/main.less b/public/css/main.less index d22fc4ea05..027540edec 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -719,7 +719,6 @@ iframe.iphone { -ms-transition: background .2s ease-in-out, border .2s ease-in-out; -o-transition: background .2s ease-in-out, border .2s ease-in-out; transition: background .2s ease-in-out, border .2s ease-in-out; - } .hamburger { @@ -751,6 +750,14 @@ iframe.iphone { .big-ion-up-arrow { font-size: 45px; margin-top: -10px; + text-align: center; + margin-bottom: -15px; +} + +.story-up-votes { + padding-top: 0px; + margin-left: -5px; + text-align: center; } //uncomment this to see the dimensions of all elements outlined in red diff --git a/public/js/application.js b/public/js/application.js index f792928b2b..eeebfa316b 100644 --- a/public/js/application.js +++ b/public/js/application.js @@ -15,5 +15,5 @@ //= require lib/jquery-2.1.1.min //= require lib/bootstrap.min -//= require lib/together/togetherjs +//= require lib/moment/moment //= require main diff --git a/seed_data/stories.json b/seed_data/stories.json index fd1a3a1c4f..ccaedb7bd7 100644 --- a/seed_data/stories.json +++ b/seed_data/stories.json @@ -5,36 +5,39 @@ "body": "cats love keyboards you know", "rank": 0, "upVotes": [], + "timePosted": 1425429660000, "author": { - "username": "terakilobyte", + "username": "testuser", "userId": "a2ad135e2aa27c14fc73ee11", - "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" + "picture": "http://www.cutecatgifs.com/wp-content/uploads/2013/11/little_guy_sleeping_in_lap.gif" }, "comments": [] }, - { - "headline": "Cat sits on keyboard 1", - "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", - "body": "cats love keyboards you know", - "rank": 1, - "upVotes": [], - "author": { - "username": "terakilobyte", - "userId": "a2ad135e2aa27c14fc73ee22", - "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" - }, - "comments": [] + { + "headline": "Cat sits on keyboard 1", + "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", + "body": "cats love keyboards you know", + "rank": 1, + "upVotes": [], + "timePosted": 1425429660000, + "author": { + "username": "testuser", + "userId": "a2ad135e2aa27c14fc73ee22", + "picture": "http://www.cutecatgifs.com/wp-content/uploads/2013/11/little_guy_sleeping_in_lap.gif" }, + "comments": [] + }, { "headline": "Cat sits on keyboard 3", "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", "body": "cats love keyboards you know", "rank": 3, "upVotes": [], + "timePosted": 1425429660000, "author": { - "username": "terakilobyte", + "username": "testuser", "userId": "a2ad135e2aa27c14fc73ee33", - "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" + "picture": "http://www.cutecatgifs.com/wp-content/uploads/2013/11/little_guy_sleeping_in_lap.gif" }, "comments": [] }, @@ -44,12 +47,15 @@ "body": "ipsizzle dolizzle sit amet, ghetto adipiscing elit. Nullam fo shizzle velizzle, aliquet volutpizzle, suscipizzle shiz, gravida vizzle, arcu. Pellentesque izzle tortor. Sizzle doggy. Boom shackalack izzle dolizzle dapibizzle ass tempizzle tellivizzle. Ma nizzle we gonna chung shiz izzle tellivizzle. Vestibulum dizzle tortor. Pellentesque pimpin' rhoncus you son of a bizzle. In dizzle habitasse platea dictumst. Donec dapibizzle. Curabitizzle pot yippiyo, pretizzle da bomb, mattis izzle, da bomb vitae, nunc. Ass suscipizzle. Cool sempizzle bow wow wow sed purus.", "rank": 20, "upVotes": [], + "timePosted": 1425429660000, "author": { - "username": "terakilobyte", + "username": "testuser", "userId": "a2ad135e2aa27c14fc73ee44", - "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" + "picture": "http://www.cutecatgifs.com/wp-content/uploads/2013/11/little_guy_sleeping_in_lap.gif" }, - "comments": ["54f61b0e43f0c2b90f162ec4"] + "comments": [ + "54f61b0e43f0c2b90f162ec4" + ] }, { "headline": "Cat sits on keyboard 2", @@ -57,10 +63,11 @@ "body": "cats love keyboards you know", "rank": 2, "upVotes": [], + "timePosted": 1425429660000, "author": { - "username": "terakilobyte", + "username": "testuser", "userId": "a2ad135e2aa27c14fc73ee55", - "picture": "https://www.google.com/search?q=cat+photo+google+images&tbm=isch&imgil=7dFgV4OZlJObjM%253A%253BDGoqtUgH7IKDWM%253Bhttp%25253A%25252F%25252Fwww.wired.co.uk%25252Fnews%25252Farchive%25252F2012-06%25252F26%25252Fgoogle-brain-recognises-cats&source=iu&pf=m&fir=7dFgV4OZlJObjM%253A%252CDGoqtUgH7IKDWM%252C_&usg=__yxi54C0GOssHCOLnh1StLAH7KNk%3D&biw=1920&bih=981&ved=0CDYQyjc&ei=n3n1VL6ENcHZoATjvYDABQ#imgrc=7dFgV4OZlJObjM%253A%3BDGoqtUgH7IKDWM%3Bhttp%253A%252F%252Fcdni.wired.co.uk%252F620x413%252Fs_v%252Fshutterstock_65735200.jpg%3Bhttp%253A%252F%252Fwww.wired.co.uk%252Fnews%252Farchive%252F2012-06%252F26%252Fgoogle-brain-recognises-cats%3B620%3B413" + "picture": "http://www.cutecatgifs.com/wp-content/uploads/2013/11/little_guy_sleeping_in_lap.gif" }, "comments": [] } diff --git a/views/partials/navbar.jade b/views/partials/navbar.jade index 221abeebf0..9c19b96b52 100644 --- a/views/partials/navbar.jade +++ b/views/partials/navbar.jade @@ -16,7 +16,7 @@ li a(href='/chat') Chat li - a(href='http://forum.freecodecamp.com' target='_blank') Forum + a(href='/stories') News li a(href='/bonfires') Bonfires if !user diff --git a/views/post/posts.jade b/views/post/posts.jade deleted file mode 100644 index 84ddbedb23..0000000000 --- a/views/post/posts.jade +++ /dev/null @@ -1,37 +0,0 @@ -h3 - ul#story-list.story-list - - script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") - script. - var getLinkedName = function getLinkedName(name) { - return name.toLowerCase().replace(/\s/g, '-'); - } - $.ajax({ - url: '/stories/index', - type: 'GET' - }) - .success( - function(data) { - for (var i = 0; i < data.length; i++) { - var li = document.createElement('li'); - var linkedName = getLinkedName(data[i].headline); - var rank = data[i].rank; - - $(li).html("
" + - "
" + - "" + - "
" + - "" + - "
" + - "
" + - "
" + rank + "
" + - "
" + data[i].author.username + "
" + - "
" + - ""); - $(li).appendTo($('#story-list')); - } - }); \ No newline at end of file diff --git a/views/post/show.jade b/views/post/show.jade deleted file mode 100644 index 3ce14c3c13..0000000000 --- a/views/post/show.jade +++ /dev/null @@ -1,47 +0,0 @@ -extends ../layout -block content - script. - var challengeName = 'Camper News'; - var storyId = !{JSON.stringify(id)}; - var comments = !{JSON.stringify(comments)}; - var upVotes = !{JSON.stringify(upVotes)}; - - .jumbotron - .row - .col-xs-2(style='position: relative; top: 50%; -webkit-transform: translateY(50%); -ms-transform: translateY(50%);transform: translateY(50%);') - h3 - a#upvote - i.ion-arrow-up-b - .col-xs-10.text-center - h1= title - .row - .col-xs-2 - h3#storyRank= rank - - .col-xs-10.text-center - h3= body - .row.negative-35 - h3.col-xs-12.col-md-6.col-lg-5.text-center Comments - .row - .col-xs-12.col-md-6.col-lg-5 - textarea#comment-box.form-control(name="comment-box", rows=5) - if (user) - script. - var user = !{JSON.stringify(user)}; - $('#upvote').unbind('click'); - .row - .col-xs-12.col-md-7.col-lg-4.text-left - include ./comments - - - - - -// - title: story.headline, - dashedName: story.link, - author: story.author, - body: story.body, - rank: story.rank, - upVotes: story.upVotes, - comments: story.comments \ No newline at end of file diff --git a/views/post/comments.jade b/views/stories/comments.jade similarity index 100% rename from views/post/comments.jade rename to views/stories/comments.jade diff --git a/views/post/index.jade b/views/stories/index.jade similarity index 86% rename from views/post/index.jade rename to views/stories/index.jade index 6f4656f1d2..a155c1de1d 100644 --- a/views/post/index.jade +++ b/views/stories/index.jade @@ -5,4 +5,4 @@ block content .panel.panel-info .panel-heading.text-center Camper News .panel-body.hug-top - include ./posts \ No newline at end of file + include ./stories \ No newline at end of file diff --git a/views/stories/show.jade b/views/stories/show.jade new file mode 100644 index 0000000000..266568f304 --- /dev/null +++ b/views/stories/show.jade @@ -0,0 +1,74 @@ +extends ../layout +block content + script. + var challengeName = 'Camper News'; + var storyId = !{JSON.stringify(id)}; + var comments = !{JSON.stringify(comments)}; + var upVotes = !{JSON.stringify(upVotes)}; + + .panel.panel-info + .panel-heading.text-center Camper News + .panel-body + h3.row.text-left + .col-xs-3.col-sm-1 + .row + .col-xs-12.big-ion-up-arrow + i.ion-arrow-up-b + h3.story-up-votes + span= rank + .col-xs-2.col-sm-1 + img(src="#{author.picture}", class='img-responsive') + .col-xs-7.col-sm-10 + .row + .col-xs-12 + a(href="#{link}") + h3= title + .col-xs-12 + span Posted #{timeAgo} + span  by  + a(href="/" + author.username) + @#{author.username} + + + + + + + + + + + + // .col-xs-2(style='position: relative; top: 50%; -webkit-transform: translateY(50%); -ms-transform: translateY(50%);transform: translateY(50%);') + // h3 + // a#upvote + // i.ion-arrow-up-b + // .col-xs-10.text-center + // h1= title + // .row + // .col-xs-2 + // h3#storyRank= rank + // + // .col-xs-10.text-center + // h3= body + //.row.negative-35 + // h3.col-xs-12.col-md-6.col-lg-5.text-center Comments + //.row + // .col-xs-12.col-md-6.col-lg-5 + // textarea#comment-box.form-control(name="comment-box", rows=5) + //if (user) + // script. + // var user = !{JSON.stringify(user)}; + // $('#upvote').unbind('click'); + //.row + // .col-xs-12.col-md-7.col-lg-4.text-left + // include ./comments + +// + title: story.headline, + dashedName: story.link, + author: story.author, + body: story.body, + rank: story.rank, + upVotes: story.upVotes, + comments: story.comments \ No newline at end of file diff --git a/views/stories/stories.jade b/views/stories/stories.jade new file mode 100644 index 0000000000..0cb846a83c --- /dev/null +++ b/views/stories/stories.jade @@ -0,0 +1,52 @@ +h3 + ul#story-list.story-list + + script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") + script. + var getLinkedName = function getLinkedName(name) { + return name.toLowerCase().replace(/\s/g, '-'); + } + $.ajax({ + url: '/stories/index', + type: 'GET' + }) + .success( + function(data) { + for (var i = 0; i < data.length; i++) { + var li = document.createElement('li'); + var linkedName = getLinkedName(data[i].headline); + var rank = data[i].rank; + + $(li).html("
" + + "
" + + "
" + + "
" + + "" + + "
" + + "
" + + "
" + + "" + rank + "" + + "
" + + "
" + + "
" + + "" + + "
" + + "
" + + "
" + + "" + + "
" + + "Posted " + + moment(data[i].timePosted).fromNow() + + " by @" + data[i].author.username + " " + + "
" + + "
" + + "
" + + "
" + + ""); + $(li).appendTo($('#story-list')); + } + }); \ No newline at end of file From ba97c984e795ba9f58e33f13a814a5b015571c56 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Thu, 5 Mar 2015 07:49:55 +0900 Subject: [PATCH 11/49] Improve view for individual news story --- public/css/main.less | 4 ++++ views/stories/show.jade | 21 ++++++++++----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/public/css/main.less b/public/css/main.less index 027540edec..d22dd766b9 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -180,6 +180,10 @@ ul { margin-top: -10px; } +.negative-28 { + margin-top: -28px; +} + .negative-35 { margin-top: -35px; } diff --git a/views/stories/show.jade b/views/stories/show.jade index 266568f304..0f934e6c10 100644 --- a/views/stories/show.jade +++ b/views/stories/show.jade @@ -9,9 +9,9 @@ block content .panel.panel-info .panel-heading.text-center Camper News .panel-body - h3.row.text-left + h3.row.text-left.negative-10 .col-xs-3.col-sm-1 - .row + .row.negative-5 .col-xs-12.big-ion-up-arrow i.ion-arrow-up-b h3.story-up-votes @@ -19,15 +19,14 @@ block content .col-xs-2.col-sm-1 img(src="#{author.picture}", class='img-responsive') .col-xs-7.col-sm-10 - .row - .col-xs-12 - a(href="#{link}") - h3= title - .col-xs-12 - span Posted #{timeAgo} - span  by  - a(href="/" + author.username) - @#{author.username} + .col-xs-12.negative-28 + a(href="#{link}") + h3= title + .col-xs-12.negative-15 + span Posted #{timeAgo} + span  by  + a(href="/" + author.username) + @#{author.username} From 3fac5cb70ea6d616007bc9fb9790b9320aa8c530 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Thu, 5 Mar 2015 09:13:22 +0900 Subject: [PATCH 12/49] Improve comment view, re-enable upvoting for articles --- public/css/main.less | 1 - public/js/main.js | 2 ++ views/stories/comments.jade | 45 ++++++++++++++++++++++++++++------- views/stories/show.jade | 47 ++++++++++--------------------------- 4 files changed, 51 insertions(+), 44 deletions(-) diff --git a/public/css/main.less b/public/css/main.less index d22dd766b9..eb47aa1ee0 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -754,7 +754,6 @@ iframe.iphone { .big-ion-up-arrow { font-size: 45px; margin-top: -10px; - text-align: center; margin-bottom: -15px; } diff --git a/public/js/main.js b/public/js/main.js index 8f7c26ba39..d6d5516306 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -143,6 +143,8 @@ $(document).ready(function() { .done(function (data, textStatus, xhr) { $('#storyRank').text(data.rank); }); + } else { + console.log('Can\'t upvote because you\'ve already upvoted'); } }; $('#upvote').on('click', upvoteHandler); diff --git a/views/stories/comments.jade b/views/stories/comments.jade index 0efc3dbdac..2a2e5a8d55 100644 --- a/views/stories/comments.jade +++ b/views/stories/comments.jade @@ -1,8 +1,8 @@ -h6 - ul#comment-list.comment-list +h3.text-left + div#comment-list.comment-list script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") - + script(src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/moment.min.js") script. var commentDetails; R.forEach(function displayComments(comment) { @@ -20,11 +20,40 @@ h6 success: function (data, textStatus, xhr) { console.log(data); commentDetails = data; - var li = document.createElement('li'); - $(li) - .html("
" + commentDetails.body + - "
" + commentDetails.rank + "
" - + commentDetails.author.username + "
") + var div = document.createElement('div'); + $(div) + .html("" + + "
" + + "
" + + "
" + + "
" + + "" + + "
" + + "
" + + "
" + + "
" + + commentDetails.rank + + "
" + + "
" + + "
" + + "
" + + "" + + "
" + + "
" + + "
" + + "
" + + commentDetails.body + + "
" + + "
" + + "
" + + "
" + + "commented " + moment(commentDetails.commentedOn).fromNow() + " by " + + "@" + commentDetails.author.username + "" + + "
" + + "
" + + "
" + + "
" + + "
") .appendTo($('#comment-list')); } diff --git a/views/stories/show.jade b/views/stories/show.jade index 0f934e6c10..9024bf9711 100644 --- a/views/stories/show.jade +++ b/views/stories/show.jade @@ -10,12 +10,13 @@ block content .panel-heading.text-center Camper News .panel-body h3.row.text-left.negative-10 - .col-xs-3.col-sm-1 + .col-xs-3.col-sm-1.text-center .row.negative-5 .col-xs-12.big-ion-up-arrow - i.ion-arrow-up-b + a#upvote + i.ion-arrow-up-b h3.story-up-votes - span= rank + span#storyRank= rank .col-xs-2.col-sm-1 img(src="#{author.picture}", class='img-responsive') .col-xs-7.col-sm-10 @@ -29,39 +30,15 @@ block content @#{author.username} + .col-xs-12 + h3.row + .col-xs-12 + textarea#comment-box.form-control(name="comment-box", rows=5) + h3.row.btn-nav.text-center + .col-xs-6.col-xs-offset-3 + .btn.btn-block.btn-primary#comment-button Comment - - - - - - - - - // .col-xs-2(style='position: relative; top: 50%; -webkit-transform: translateY(50%); -ms-transform: translateY(50%);transform: translateY(50%);') - // h3 - // a#upvote - // i.ion-arrow-up-b - // .col-xs-10.text-center - // h1= title - // .row - // .col-xs-2 - // h3#storyRank= rank - // - // .col-xs-10.text-center - // h3= body - //.row.negative-35 - // h3.col-xs-12.col-md-6.col-lg-5.text-center Comments - //.row - // .col-xs-12.col-md-6.col-lg-5 - // textarea#comment-box.form-control(name="comment-box", rows=5) - //if (user) - // script. - // var user = !{JSON.stringify(user)}; - // $('#upvote').unbind('click'); - //.row - // .col-xs-12.col-md-7.col-lg-4.text-left - // include ./comments + include ./comments // title: story.headline, From 6a9bfc5ebf62c0eae13a4f52f9449a03a17ae302 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Wed, 4 Mar 2015 23:44:30 -0800 Subject: [PATCH 13/49] remove together.js and add a non-functioning submit form to the index view of news --- public/css/main.less | 1 + public/js/lib/together/togetherjs.js | 786 --------------------------- views/stories/index.jade | 3 +- views/stories/submit.jade | 11 + 4 files changed, 14 insertions(+), 787 deletions(-) delete mode 100644 public/js/lib/together/togetherjs.js create mode 100644 views/stories/submit.jade diff --git a/public/css/main.less b/public/css/main.less index eb47aa1ee0..83c26034e6 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -755,6 +755,7 @@ iframe.iphone { font-size: 45px; margin-top: -10px; margin-bottom: -15px; + text-align: center; } .story-up-votes { diff --git a/public/js/lib/together/togetherjs.js b/public/js/lib/together/togetherjs.js deleted file mode 100644 index 5d8412c3b8..0000000000 --- a/public/js/lib/together/togetherjs.js +++ /dev/null @@ -1,786 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/*jshint scripturl:true */ -(function () { - - var defaultConfiguration = { - // Disables clicks for a certain element. - // (e.g., 'canvas' would not show clicks on canvas elements.) - // Setting this to true will disable clicks globally. - dontShowClicks: false, - // Experimental feature to echo clicks to certain elements across clients: - cloneClicks: false, - // Enable Mozilla or Google analytics on the page when TogetherJS is activated: - // FIXME: these don't seem to be working, and probably should be removed in favor - // of the hub analytics - enableAnalytics: false, - // The code to enable (this is defaulting to a Mozilla code): - analyticsCode: "UA-55446531-1", - // The base URL of the hub (gets filled in below): - hubBase: "https://fcctogether.herokuapp.com", - // A function that will return the name of the user: - getUserName: null, - // A function that will return the color of the user: - getUserColor: null, - // A function that will return the avatar of the user: - getUserAvatar: null, - // The siteName is used in the walkthrough (defaults to document.title): - siteName: null, - // Whether to use the minimized version of the code (overriding the built setting) - useMinimizedCode: undefined, - // Any events to bind to - on: {}, - // Hub events to bind to - hub_on: {}, - // Enables the alt-T alt-T TogetherJS shortcut; however, this setting - // must be enabled early as TogetherJSConfig_enableShortcut = true; - enableShortcut: false, - // The name of this tool as provided to users. The UI is updated to use this. - // Because of how it is used in text it should be a proper noun, e.g., - // "MySite's Collaboration Tool" - toolName: null, - // Used to auto-start TogetherJS with a {prefix: pageName, max: participants} - // Also with findRoom: "roomName" it will connect to the given room name - findRoom: null, - // If true, starts TogetherJS automatically (of course!) - autoStart: false, - // If true, then the "Join TogetherJS Session?" confirmation dialog - // won't come up - suppressJoinConfirmation: false, - // If true, then the "Invite a friend" window won't automatically come up - suppressInvite: false, - // A room in which to find people to invite to this session, - inviteFromRoom: null, - // This is used to keep sessions from crossing over on the same - // domain, if for some reason you want sessions that are limited - // to only a portion of the domain: - storagePrefix: "togetherjs", - // When true, we treat the entire URL, including the hash, as the identifier - // of the page; i.e., if you one person is on `http://example.com/#view1` - // and another person is at `http://example.com/#view2` then these two people - // are considered to be at completely different URLs - includeHashInUrl: false, - // When true, the WebRTC-based mic/chat will be disabled - disableWebRTC: false, - // When true, youTube videos will synchronize - youtube: true, - // Ignores the following console messages, disables all messages if set to true - ignoreMessages: ["cursor-update", "keydown", "scroll-update"], - // Ignores the following forms (will ignore all forms if set to true): - ignoreForms: [":password"] -}; - - var styleSheet = "/togetherjs/togetherjs.css"; - - var baseUrl = "https://togetherjs.com"; - if (baseUrl == "__" + "baseUrl__") { - // Reset the variable if it doesn't get substituted - baseUrl = ""; - } - // True if this file should use minimized sub-resources: - var min = "no" == "__" + "min__" ? false : "no" == "yes"; - - var baseUrlOverride = localStorage.getItem("togetherjs.baseUrlOverride"); - if (baseUrlOverride) { - try { - baseUrlOverride = JSON.parse(baseUrlOverride); - } catch (e) { - baseUrlOverride = null; - } - if ((! baseUrlOverride) || baseUrlOverride.expiresAt < Date.now()) { - // Ignore because it has expired - localStorage.removeItem("togetherjs.baseUrlOverride"); - } else { - baseUrl = baseUrlOverride.baseUrl; - var logger = console.warn || console.log; - logger.call(console, "Using TogetherJS baseUrlOverride:", baseUrl); - logger.call(console, "To undo run: localStorage.removeItem('togetherjs.baseUrlOverride')"); - } - } - - var configOverride = localStorage.getItem("togetherjs.configOverride"); - if (configOverride) { - try { - configOverride = JSON.parse(configOverride); - } catch (e) { - configOverride = null; - } - if ((! configOverride) || configOverride.expiresAt < Date.now()) { - localStorage.removeItem("togetherjs.configOverride"); - } else { - var shownAny = false; - for (var attr in configOverride) { - if (attr == "expiresAt" || ! configOverride.hasOwnProperty(attr)) { - continue; - } - if (! shownAny) { - console.warn("Using TogetherJS configOverride"); - console.warn("To undo run: localStorage.removeItem('togetherjs.configOverride')"); - } - window["TogetherJSConfig_" + attr] = configOverride[attr]; - console.log("Config override:", attr, "=", configOverride[attr]); - } - } - } - - var version = "unknown"; - // FIXME: we could/should use a version from the checkout, at least - // for production - var cacheBust = ""; - if ((! cacheBust) || cacheBust == "") { - cacheBust = Date.now() + ""; - } else { - version = cacheBust; - } - - // Make sure we have all of the console.* methods: - if (typeof console == "undefined") { - console = {}; - } - if (! console.log) { - console.log = function () {}; - } - ["debug", "info", "warn", "error"].forEach(function (method) { - if (! console[method]) { - console[method] = console.log; - } - }); - - if (! baseUrl) { - var scripts = document.getElementsByTagName("script"); - for (var i=0; i with togetherjs.js and togetherjs-min.js)"); - } - - function addStyle() { - var existing = document.getElementById("togetherjs-stylesheet"); - if (! existing) { - var link = document.createElement("link"); - link.id = "togetherjs-stylesheet"; - link.setAttribute("rel", "stylesheet"); - link.href = baseUrl + styleSheet + "?bust=" + cacheBust; - document.head.appendChild(link); - } - } - - function addScript(url) { - var script = document.createElement("script"); - script.src = baseUrl + url + "?bust=" + cacheBust; - document.head.appendChild(script); - } - - var TogetherJS = window.TogetherJS = function TogetherJS(event) { - if (TogetherJS.running) { - var session = TogetherJS.require("session"); - session.close(); - return; - } - TogetherJS.startup.button = null; - try { - if (event && typeof event == "object") { - if (event.target && typeof event) { - TogetherJS.startup.button = event.target; - } else if (event.nodeType == 1) { - TogetherJS.startup.button = event; - } else if (event[0] && event[0].nodeType == 1) { - // Probably a jQuery element - TogetherJS.startup.button = event[0]; - } - } - } catch (e) { - console.warn("Error determining starting button:", e); - } - if (window.TowTruckConfig) { - console.warn("TowTruckConfig is deprecated; please use TogetherJSConfig"); - if (window.TogetherJSConfig) { - console.warn("Ignoring TowTruckConfig in favor of TogetherJSConfig"); - } else { - window.TogetherJSConfig = TowTruckConfig; - } - } - if (window.TogetherJSConfig && (! window.TogetherJSConfig.loaded)) { - TogetherJS.config(window.TogetherJSConfig); - window.TogetherJSConfig.loaded = true; - } - - // This handles loading configuration from global variables. This - // includes TogetherJSConfig_on_*, which are attributes folded into - // the "on" configuration value. - var attr; - var attrName; - var globalOns = {}; - for (attr in window) { - if (attr.indexOf("TogetherJSConfig_on_") === 0) { - attrName = attr.substr(("TogetherJSConfig_on_").length); - globalOns[attrName] = window[attr]; - } else if (attr.indexOf("TogetherJSConfig_") === 0) { - attrName = attr.substr(("TogetherJSConfig_").length); - TogetherJS.config(attrName, window[attr]); - } else if (attr.indexOf("TowTruckConfig_on_") === 0) { - attrName = attr.substr(("TowTruckConfig_on_").length); - console.warn("TowTruckConfig_* is deprecated, please rename", attr, "to TogetherJSConfig_on_" + attrName); - globalOns[attrName] = window[attr]; - } else if (attr.indexOf("TowTruckConfig_") === 0) { - attrName = attr.substr(("TowTruckConfig_").length); - console.warn("TowTruckConfig_* is deprecated, please rename", attr, "to TogetherJSConfig_" + attrName); - TogetherJS.config(attrName, window[attr]); - } - - - } - // FIXME: copy existing config? - // FIXME: do this directly in TogetherJS.config() ? - // FIXME: close these configs? - var ons = TogetherJS.config.get("on"); - for (attr in globalOns) { - if (globalOns.hasOwnProperty(attr)) { - // FIXME: should we avoid overwriting? Maybe use arrays? - ons[attr] = globalOns[attr]; - } - } - TogetherJS.config("on", ons); - for (attr in ons) { - TogetherJS.on(attr, ons[attr]); - } - var hubOns = TogetherJS.config.get("hub_on"); - if (hubOns) { - for (attr in hubOns) { - if (hubOns.hasOwnProperty(attr)) { - TogetherJS.hub.on(attr, hubOns[attr]); - } - } - } - - if (! TogetherJS.startup.reason) { - // Then a call to TogetherJS() from a button must be started TogetherJS - TogetherJS.startup.reason = "started"; - } - - // FIXME: maybe I should just test for TogetherJS.require: - if (TogetherJS._loaded) { - var session = TogetherJS.require("session"); - addStyle(); - session.start(); - return; - } - // A sort of signal to session.js to tell it to actually - // start itself (i.e., put up a UI and try to activate) - TogetherJS.startup._launch = true; - - addStyle(); - var minSetting = TogetherJS.config.get("useMinimizedCode"); - TogetherJS.config.close("useMinimizedCode"); - if (minSetting !== undefined) { - min = !! minSetting; - } - var requireConfig = TogetherJS._extend(TogetherJS.requireConfig); - var deps = ["session", "jquery"]; - function callback(session, jquery) { - TogetherJS._loaded = true; - if (! min) { - TogetherJS.require = require.config({context: "togetherjs"}); - TogetherJS._requireObject = require; - } - } - if (! min) { - if (typeof require == "function") { - if (! require.config) { - console.warn("The global require (", require, ") is not requirejs; please use togetherjs-min.js"); - throw new Error("Conflict with window.require"); - } - TogetherJS.require = require.config(requireConfig); - } - } - if (typeof TogetherJS.require == "function") { - // This is an already-configured version of require - TogetherJS.require(deps, callback); - } else { - requireConfig.deps = deps; - requireConfig.callback = callback; - if (! min) { - window.require = requireConfig; - } - } - if (min) { - addScript("/togetherjs/togetherjsPackage.js"); - } else { - addScript("/togetherjs/libs/require.js"); - } - }; - - TogetherJS.pageLoaded = Date.now(); - - TogetherJS._extend = function (base, extensions) { - if (! extensions) { - extensions = base; - base = {}; - } - for (var a in extensions) { - if (extensions.hasOwnProperty(a)) { - base[a] = extensions[a]; - } - } - return base; - }; - - TogetherJS._startupInit = { - // What element, if any, was used to start the session: - button: null, - // The startReason is the reason TogetherJS was started. One of: - // null: not started - // started: hit the start button (first page view) - // joined: joined the session (first page view) - reason: null, - // Also, the session may have started on "this" page, or maybe is continued - // from a past page. TogetherJS.continued indicates the difference (false the - // first time TogetherJS is started or joined, true on later page loads). - continued: false, - // This is set to tell the session what shareId to use, if the boot - // code knows (mostly because the URL indicates the id). - _joinShareId: null, - // This tells session to start up immediately (otherwise it would wait - // for session.start() to be run) - _launch: false - }; - TogetherJS.startup = TogetherJS._extend(TogetherJS._startupInit); - TogetherJS.running = false; - - TogetherJS.requireConfig = { - context: "togetherjs", - baseUrl: baseUrl + "/togetherjs", - urlArgs: "bust=" + cacheBust, - paths: { - jquery: "libs/jquery-1.8.3.min", - walkabout: "libs/walkabout/walkabout", - esprima: "libs/walkabout/lib/esprima", - falafel: "libs/walkabout/lib/falafel", - tinycolor: "libs/tinycolor", - whrandom: "libs/whrandom/random" - } - }; - - TogetherJS._mixinEvents = function (proto) { - proto.on = function on(name, callback) { - if (typeof callback != "function") { - console.warn("Bad callback for", this, ".once(", name, ", ", callback, ")"); - throw "Error: .once() called with non-callback"; - } - if (name.search(" ") != -1) { - var names = name.split(/ +/g); - names.forEach(function (n) { - this.on(n, callback); - }, this); - return; - } - if (this._knownEvents && this._knownEvents.indexOf(name) == -1) { - var thisString = "" + this; - if (thisString.length > 20) { - thisString = thisString.substr(0, 20) + "..."; - } - console.warn(thisString + ".on('" + name + "', ...): unknown event"); - if (console.trace) { - console.trace(); - } - } - if (! this._listeners) { - this._listeners = {}; - } - if (! this._listeners[name]) { - this._listeners[name] = []; - } - if (this._listeners[name].indexOf(callback) == -1) { - this._listeners[name].push(callback); - } - }; - proto.once = function once(name, callback) { - if (typeof callback != "function") { - console.warn("Bad callback for", this, ".once(", name, ", ", callback, ")"); - throw "Error: .once() called with non-callback"; - } - var attr = "onceCallback_" + name; - // FIXME: maybe I should add the event name to the .once attribute: - if (! callback[attr]) { - callback[attr] = function onceCallback() { - callback.apply(this, arguments); - this.off(name, onceCallback); - delete callback[attr]; - }; - } - this.on(name, callback[attr]); - }; - proto.off = proto.removeListener = function off(name, callback) { - if (this._listenerOffs) { - // Defer the .off() call until the .emit() is done. - this._listenerOffs.push([name, callback]); - return; - } - if (name.search(" ") != -1) { - var names = name.split(/ +/g); - names.forEach(function (n) { - this.off(n, callback); - }, this); - return; - } - if ((! this._listeners) || ! this._listeners[name]) { - return; - } - var l = this._listeners[name], _len = l.length; - for (var i=0; i<_len; i++) { - if (l[i] == callback) { - l.splice(i, 1); - break; - } - } - }; - proto.emit = function emit(name) { - var offs = this._listenerOffs = []; - if ((! this._listeners) || ! this._listeners[name]) { - return; - } - var args = Array.prototype.slice.call(arguments, 1); - var l = this._listeners[name]; - l.forEach(function (callback) { - - callback.apply(this, args); - }, this); - delete this._listenerOffs; - if (offs.length) { - offs.forEach(function (item) { - this.off(item[0], item[1]); - }, this); - } - - }; - return proto; - }; - - /* This finalizes the unloading of TogetherJS, including unloading modules */ - TogetherJS._teardown = function () { - var requireObject = TogetherJS._requireObject || window.require; - // FIXME: this doesn't clear the context for min-case - if (requireObject.s && requireObject.s.contexts) { - delete requireObject.s.contexts.togetherjs; - } - TogetherJS._loaded = false; - TogetherJS.startup = TogetherJS._extend(TogetherJS._startupInit); - TogetherJS.running = false; - }; - - TogetherJS._mixinEvents(TogetherJS); - TogetherJS._knownEvents = ["ready", "close"]; - TogetherJS.toString = function () { - return "TogetherJS"; - }; - - TogetherJS._configuration = {}; - TogetherJS._defaultConfiguration = defaultConfiguration; - TogetherJS._configTrackers = {}; - TogetherJS._configClosed = {}; - - /* TogetherJS.config(configurationObject) - or: TogetherJS.config(configName, value) - - Adds configuration to TogetherJS. You may also set the global variable TogetherJSConfig - and when TogetherJS is started that configuration will be loaded. - - Unknown configuration values will lead to console error messages. - */ - TogetherJS.config = function (name, maybeValue) { - var settings; - if (arguments.length == 1) { - if (typeof name != "object") { - throw new Error('TogetherJS.config(value) must have an object value (not: ' + name + ')'); - } - settings = name; - } else { - settings = {}; - settings[name] = maybeValue; - } - var i; - var tracker; - for (var attr in settings) { - if (settings.hasOwnProperty(attr)) { - if (TogetherJS._configClosed[attr] && TogetherJS.running) { - throw new Error("The configuration " + attr + " is finalized and cannot be changed"); - } - } - } - for (var attr in settings) { - if (! settings.hasOwnProperty(attr)) { - continue; - } - if (attr == "loaded" || attr == "callToStart") { - continue; - } - if (! TogetherJS._defaultConfiguration.hasOwnProperty(attr)) { - console.warn("Unknown configuration value passed to TogetherJS.config():", attr); - } - var previous = TogetherJS._configuration[attr]; - var value = settings[attr]; - TogetherJS._configuration[attr] = value; - var trackers = TogetherJS._configTrackers[name] || []; - var failed = false; - for (i=0; i Date: Thu, 5 Mar 2015 19:21:26 +0900 Subject: [PATCH 14/49] Need to work on story redirect after submission, fix url link in stories/show --- app.js | 5 ++++ controllers/story.js | 50 ++++++++++++++++++++++++++++++++++++-- models/Story.js | 10 +++++++- public/css/main.less | 13 ++++++++++ public/js/main.js | 39 ++++++++++++++++++++++++++++- seed_data/stories.json | 38 ++++++++++++++++++----------- views/stories/index.jade | 5 ++-- views/stories/stories.jade | 4 +-- views/stories/submit.jade | 27 ++++++++++++-------- 9 files changed, 159 insertions(+), 32 deletions(-) diff --git a/app.js b/app.js index bbff557451..552e0d6e0c 100644 --- a/app.js +++ b/app.js @@ -294,6 +294,11 @@ app.get( storyController.comments ); +app.post( + '/stories/submit', + storyController.storySubmission +); + app.get( '/stories/:storyName', storyController.returnIndividualStory diff --git a/controllers/story.js b/controllers/story.js index 36ae133107..581bb5e50b 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -56,13 +56,14 @@ exports.returnIndividualStory = function(req, res, next) { title: story.headline, link: story.link, author: story.author, - body: story.body, + description: story.description, rank: story.rank, upVotes: story.upVotes, comments: story.comments, id: story._id, user: req.user, - timeAgo: moment(story.timePosted).fromNow() + timeAgo: moment(story.timePosted).fromNow(), + image: story.image }); }); }; @@ -96,3 +97,48 @@ exports.comments = function(req, res, next) { return res.send(comment); }); }; + +/* + + author: {}, + comments: { + type: Array, + default: [] + }, + image: + */ + +exports.storySubmission = function(req, res, next) { + var data = req.body.data; + var storyLink = data.headline + .replace(/\'/g, '') + .replace(/\"/g, '') + .replace(/,/g, '') + .replace(/[^a-z0-9]/gi, ' ') + .replace(/\s+/g, ' ') + .toLowerCase(); + var story = new Story({ + headline: data.headline, + timePosted: Date.now(), + link: data.link, + description: data.description, + rank: 0, + upVotes: 0, + author: data.author, + comments: [], + image: data.image, + storyLink: storyLink + }); + + debug('this is a story', story); + + story.save(function(err, data) { + if (err) { + throw err; + } + res.send(JSON.stringify({ + storyLink: story.storyLink.replace(/\s/g, '-').toLowerCase() + })); + }); + +}; diff --git a/models/Story.js b/models/Story.js index 79e41d774e..902ff2c893 100644 --- a/models/Story.js +++ b/models/Story.js @@ -14,7 +14,7 @@ var storySchema = new mongoose.Schema({ type: String, unique: false }, - body: { + description: { type: String, unique: false }, @@ -30,6 +30,14 @@ var storySchema = new mongoose.Schema({ comments: { type: Array, default: [] + }, + image: { + type: String, + default: '' + }, + storyLink: { + type: String, + default: '' } }); diff --git a/public/css/main.less b/public/css/main.less index 83c26034e6..55dbdd2332 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -764,6 +764,19 @@ iframe.iphone { text-align: center; } +.control-label .control-label-story-submission { + telt-align: left; +} + +.img-story-post { + max-width: 110px; + max-height: 110px; +} + +.spacer { + padding: 15px 0 15px 0; +} + //uncomment this to see the dimensions of all elements outlined in red //* { // border-color: red; diff --git a/public/js/main.js b/public/js/main.js index d6d5516306..0b3dee6fb0 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -55,7 +55,7 @@ $(document).ready(function() { if (res) { window.location.href = '/bonfires' } - }) + }); } } @@ -148,6 +148,43 @@ $(document).ready(function() { } }; $('#upvote').on('click', upvoteHandler); + + var storySubmitButtonHandler = function storySubmitButtonHandler() { + var data = $('#story-submission-form :input'); + var link = $(data[0]).val(); + var headline = $(data[1]).val(); + $('#story-submit').unbind('click'); + $.post('/stories/submit', + { + data: { + link: link, + headline: headline, + timePosted: Date.now(), + description: 'TODO', + + rank: 0, + upVotes: 0, + author: { + picture: user.profile.picture, + userId: user._id, + username: user.profile.username + }, + comments: [], + image: 'http://rossmounce.co.uk/wp-content/uploads/2014/11/grumpy-cat-no-1.jpg' + } + }) + .fail(function (xhr, textStatus, errorThrown) { + $('#story-submit').bind('click', storySubmitButtonHandler); + }) + .done(function (data, textStatus, xhr) { + window.dataReturn = data; + console.log(data); + window.location = '/stories/' + data.storyLink; + }); + + }; + + $('#story-submit').on('click', storySubmitButtonHandler); }); var profileValidation = angular.module('profileValidation',['ui.bootstrap']); diff --git a/seed_data/stories.json b/seed_data/stories.json index ccaedb7bd7..246950ec44 100644 --- a/seed_data/stories.json +++ b/seed_data/stories.json @@ -1,8 +1,9 @@ [ { "headline": "Cat sits on keyboard 0", + "storyLink": "cat sits on keyboard 0", "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", - "body": "cats love keyboards you know", + "description": "cats love keyboards you know", "rank": 0, "upVotes": [], "timePosted": 1425429660000, @@ -11,12 +12,14 @@ "userId": "a2ad135e2aa27c14fc73ee11", "picture": "http://www.cutecatgifs.com/wp-content/uploads/2013/11/little_guy_sleeping_in_lap.gif" }, - "comments": [] - }, + "comments": [], + "image": "http://rossmounce.co.uk/wp-content/uploads/2014/11/grumpy-cat-no-1.jpg" +}, { "headline": "Cat sits on keyboard 1", + "storyLink": "cat sits on keyboard 1", "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", - "body": "cats love keyboards you know", + "description": "cats love keyboards you know", "rank": 1, "upVotes": [], "timePosted": 1425429660000, @@ -25,12 +28,14 @@ "userId": "a2ad135e2aa27c14fc73ee22", "picture": "http://www.cutecatgifs.com/wp-content/uploads/2013/11/little_guy_sleeping_in_lap.gif" }, - "comments": [] - }, + "comments": [], + "image": "http://rossmounce.co.uk/wp-content/uploads/2014/11/grumpy-cat-no-1.jpg" +}, { "headline": "Cat sits on keyboard 3", + "storyLink": "cat sits on keyboard 3", "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", - "body": "cats love keyboards you know", + "description": "cats love keyboards you know", "rank": 3, "upVotes": [], "timePosted": 1425429660000, @@ -39,12 +44,14 @@ "userId": "a2ad135e2aa27c14fc73ee33", "picture": "http://www.cutecatgifs.com/wp-content/uploads/2013/11/little_guy_sleeping_in_lap.gif" }, - "comments": [] - }, + "comments": [], + "image": "http://rossmounce.co.uk/wp-content/uploads/2014/11/grumpy-cat-no-1.jpg" +}, { "headline": "Cat sits on keyboard 20", + "storyLink": "cat sits on keyboard 20", "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", - "body": "ipsizzle dolizzle sit amet, ghetto adipiscing elit. Nullam fo shizzle velizzle, aliquet volutpizzle, suscipizzle shiz, gravida vizzle, arcu. Pellentesque izzle tortor. Sizzle doggy. Boom shackalack izzle dolizzle dapibizzle ass tempizzle tellivizzle. Ma nizzle we gonna chung shiz izzle tellivizzle. Vestibulum dizzle tortor. Pellentesque pimpin' rhoncus you son of a bizzle. In dizzle habitasse platea dictumst. Donec dapibizzle. Curabitizzle pot yippiyo, pretizzle da bomb, mattis izzle, da bomb vitae, nunc. Ass suscipizzle. Cool sempizzle bow wow wow sed purus.", + "description": "ipsizzle dolizzle sit amet, ghetto adipiscing elit. Nullam fo shizzle velizzle, aliquet volutpizzle, suscipizzle shiz, gravida vizzle, arcu. Pellentesque izzle tortor. Sizzle doggy. Boom shackalack izzle dolizzle dapibizzle ass tempizzle tellivizzle. Ma nizzle we gonna chung shiz izzle tellivizzle. Vestibulum dizzle tortor. Pellentesque pimpin' rhoncus you son of a bizzle. In dizzle habitasse platea dictumst. Donec dapibizzle. Curabitizzle pot yippiyo, pretizzle da bomb, mattis izzle, da bomb vitae, nunc. Ass suscipizzle. Cool sempizzle bow wow wow sed purus.", "rank": 20, "upVotes": [], "timePosted": 1425429660000, @@ -55,12 +62,14 @@ }, "comments": [ "54f61b0e43f0c2b90f162ec4" - ] - }, + ], + "image": "http://rossmounce.co.uk/wp-content/uploads/2014/11/grumpy-cat-no-1.jpg" +}, { "headline": "Cat sits on keyboard 2", + "storyLink": "cat sits on keyboard 2", "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", - "body": "cats love keyboards you know", + "description": "cats love keyboards you know", "rank": 2, "upVotes": [], "timePosted": 1425429660000, @@ -69,6 +78,7 @@ "userId": "a2ad135e2aa27c14fc73ee55", "picture": "http://www.cutecatgifs.com/wp-content/uploads/2013/11/little_guy_sleeping_in_lap.gif" }, - "comments": [] + "comments": [], + "image": "http://rossmounce.co.uk/wp-content/uploads/2014/11/grumpy-cat-no-1.jpg" } ] \ No newline at end of file diff --git a/views/stories/index.jade b/views/stories/index.jade index 39636b3d4c..f3051e4197 100644 --- a/views/stories/index.jade +++ b/views/stories/index.jade @@ -2,8 +2,9 @@ extends ../layout block content script. var challengeName = 'Camper News'; + var user = !{JSON.stringify(user)}; .panel.panel-info .panel-heading.text-center Camper News - .panel-body.hug-top - include ./stories + .panel-body include ./submit + include ./stories diff --git a/views/stories/stories.jade b/views/stories/stories.jade index 0cb846a83c..9122d66c37 100644 --- a/views/stories/stories.jade +++ b/views/stories/stories.jade @@ -14,7 +14,7 @@ h3 function(data) { for (var i = 0; i < data.length; i++) { var li = document.createElement('li'); - var linkedName = getLinkedName(data[i].headline); + var linkedName = getLinkedName(data[i].storyLink); var rank = data[i].rank; $(li).html("
" + @@ -35,7 +35,7 @@ h3 "
" + "" + "
" + diff --git a/views/stories/submit.jade b/views/stories/submit.jade index ad3259afc3..9dacdc0e55 100644 --- a/views/stories/submit.jade +++ b/views/stories/submit.jade @@ -1,11 +1,18 @@ .col-xs-12 - form.form-horizontal - .form-group.col-xs-12.text-left - .col-xs-1.col-xs-offset-2 - label.control-label(for='name') Link - .col-xs-9 - input.form-control(placeholder='Paste your link here', name='Link') - .form-group - label.col-xs-1.col-xs-offset-2.control-label(for='name') Title - .col-xs-9 - input.form-control(placeholder='Type a headline for your link here', name='Title') + form.form-horizontal.control-label-story-submission#story-submission-form + .col-xs-2.img-story-post + img.img-responsive(src='#{user.profile.picture}') + .col-xs-10 + .form-group + .col-xs-1 + label.control-label.control-label-story-submission(for='name') Link + .col-xs-11 + input.form-control(placeholder='Paste your link here', name='Link') + .form-group + .col-xs-1 + label.control-label.control-label-story-submission(for='name') Title + .col-xs-11 + input.form-control(placeholder='Type a headline for your link here', name='Title') + .form-group + .btn.btn-large.btn-block.btn-primary#story-submit Submit +.spacer \ No newline at end of file From 40a42e29c2322510545cfb74f7408c3f8ea0b37e Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Thu, 5 Mar 2015 19:40:44 +0900 Subject: [PATCH 15/49] Fixed url redirect after submission, upvoting now works --- controllers/story.js | 9 +++++---- public/js/main.js | 4 +--- views/stories/show.jade | 1 + 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/controllers/story.js b/controllers/story.js index 581bb5e50b..0e7744071a 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -29,9 +29,11 @@ exports.index = function(req, res, next) { exports.returnIndividualStory = function(req, res, next) { var dashedName = req.params.storyName; - storyName = dashedName.replace(/\-/g, ' '); + var storyName = dashedName.replace(/\-/g, ' '); - Story.find({'headline' : new RegExp(storyName, 'i')}, function(err, story) { + debug('looking for %s', storyName); + + Story.find({'storyLink' : new RegExp(storyName, 'i')}, function(err, story) { if (err) { next(err); } @@ -134,11 +136,10 @@ exports.storySubmission = function(req, res, next) { story.save(function(err, data) { if (err) { - throw err; + return res.status(500); } res.send(JSON.stringify({ storyLink: story.storyLink.replace(/\s/g, '-').toLowerCase() })); }); - }; diff --git a/public/js/main.js b/public/js/main.js index 0b3dee6fb0..8a5f8216c3 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -177,9 +177,7 @@ $(document).ready(function() { $('#story-submit').bind('click', storySubmitButtonHandler); }) .done(function (data, textStatus, xhr) { - window.dataReturn = data; - console.log(data); - window.location = '/stories/' + data.storyLink; + window.location = '/stories/' + JSON.parse(data).storyLink; }); }; diff --git a/views/stories/show.jade b/views/stories/show.jade index 9024bf9711..c02ee316d5 100644 --- a/views/stories/show.jade +++ b/views/stories/show.jade @@ -5,6 +5,7 @@ block content var storyId = !{JSON.stringify(id)}; var comments = !{JSON.stringify(comments)}; var upVotes = !{JSON.stringify(upVotes)}; + var user = !{JSON.stringify(user)}; .panel.panel-info .panel-heading.text-center Camper News From 8552a06ac641f12578f4cde1307dabeeeb2a5383 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Thu, 5 Mar 2015 19:51:56 +0900 Subject: [PATCH 16/49] Add url correcting in case user forgets http:// or https:// so url works properly in show view, remove upvote arrows from index view --- controllers/story.js | 6 +++++- views/stories/stories.jade | 5 ----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/controllers/story.js b/controllers/story.js index 0e7744071a..83c64ec94b 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -119,10 +119,14 @@ exports.storySubmission = function(req, res, next) { .replace(/[^a-z0-9]/gi, ' ') .replace(/\s+/g, ' ') .toLowerCase(); + var link = data.link; + if (link.search(/^https?:\/\//g) === -1) { + link = 'http://' + link; + } var story = new Story({ headline: data.headline, timePosted: Date.now(), - link: data.link, + link: link, description: data.description, rank: 0, upVotes: 0, diff --git a/views/stories/stories.jade b/views/stories/stories.jade index 9122d66c37..c3dcc96caa 100644 --- a/views/stories/stories.jade +++ b/views/stories/stories.jade @@ -19,11 +19,6 @@ h3 $(li).html("
" + "
" + - "
" + - "
" + - "" + - "
" + - "
" + "
" + "" + rank + "" + "
" + From 046a20963706a0aa8787fb84c0380fb00d0b68fe Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Fri, 6 Mar 2015 06:08:40 +0900 Subject: [PATCH 17/49] Comment submission now works on main story, need to refresh page after submission to display new comment --- app.js | 5 +++++ controllers/story.js | 44 ++++++++++++++++++++++++++++++++++---------- public/js/main.js | 27 +++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/app.js b/app.js index 552e0d6e0c..237cd1f65a 100644 --- a/app.js +++ b/app.js @@ -289,6 +289,11 @@ app.get( storyController.json ); +app.post( + '/stories/comment/submit', + storyController.commentSubmit +); + app.get( '/stories/comments/:id', storyController.comments diff --git a/controllers/story.js b/controllers/story.js index 83c64ec94b..8ea25044ca 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -100,16 +100,6 @@ exports.comments = function(req, res, next) { }); }; -/* - - author: {}, - comments: { - type: Array, - default: [] - }, - image: - */ - exports.storySubmission = function(req, res, next) { var data = req.body.data; var storyLink = data.headline @@ -147,3 +137,37 @@ exports.storySubmission = function(req, res, next) { })); }); }; + +exports.commentSubmit = function(req, res, next) { + var data = req.body.data; + var comment = new Comment({ + associatedPost: data.associatedPost, + body: data.body, + rank: 0, + upvotes: 0, + author: data.author, + comments: [] + }); + + comment.save(function(err, data) { + if (err) { + return res.status(500); + } + debug('this is data from save', data); + Story.find({'_id': comment.associatedPost}, function(err, associatedStory) { + if (err) { + return res.status(500); + } + associatedStory = associatedStory.pop(); + if (associatedStory) { + associatedStory.comments.push(data._id); + associatedStory.save(function(err, data) { + if (err) { + res.status(500); + } + res.send(true); + }); + } + }); + }); +}; diff --git a/public/js/main.js b/public/js/main.js index 8a5f8216c3..da5163c3e3 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -183,6 +183,33 @@ $(document).ready(function() { }; $('#story-submit').on('click', storySubmitButtonHandler); + + var commentSubmitButtonHandler = function commentSubmitButtonHandler() { + var data = $('#comment-box').val(); + + $('#comment-button').unbind('click'); + $.post('/stories/comment/submit', + { + data: { + associatedPost: storyId, + body: data, + author: { + picture: user.profile.picture, + userId: user._id, + username: user.profile.username + } + } + }) + .fail(function (xhr, textStatus, errorThrown) { + $('#comment-button').bind('click', commentSubmitButtonHandler); + }) + .done(function (data, textStatus, xhr) { + //window.location = '/stories/' + JSON.parse(data).storyLink; + }); + + }; + + $('#comment-button').on('click', commentSubmitButtonHandler); }); var profileValidation = angular.module('profileValidation',['ui.bootstrap']); From 25f589479a4e380d885d225d690745f82bcd51e4 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Thu, 5 Mar 2015 13:42:01 -0800 Subject: [PATCH 18/49] refactor camper news into 4 pages instead of one --- views/stories/{stories.jade => hot-stories.jade} | 0 views/stories/index.jade | 11 +++++++++-- views/stories/new-stories.jade | 0 views/stories/news-nav.jade | 15 +++++++++++++++ views/stories/search-stories.jade | 0 views/stories/{submit.jade => submit-story.jade} | 2 +- 6 files changed, 25 insertions(+), 3 deletions(-) rename views/stories/{stories.jade => hot-stories.jade} (100%) create mode 100644 views/stories/new-stories.jade create mode 100644 views/stories/news-nav.jade create mode 100644 views/stories/search-stories.jade rename views/stories/{submit.jade => submit-story.jade} (97%) diff --git a/views/stories/stories.jade b/views/stories/hot-stories.jade similarity index 100% rename from views/stories/stories.jade rename to views/stories/hot-stories.jade diff --git a/views/stories/index.jade b/views/stories/index.jade index f3051e4197..609cd7f32a 100644 --- a/views/stories/index.jade +++ b/views/stories/index.jade @@ -6,5 +6,12 @@ block content .panel.panel-info .panel-heading.text-center Camper News .panel-body - include ./submit - include ./stories + include ./news-nav + if (page === 'hot') + include ./hot-stories + if (page === 'new') + include ./new-stories + if (page === 'submit') + include ./submit-story + if (page === 'search') + include ./search-stories \ No newline at end of file diff --git a/views/stories/new-stories.jade b/views/stories/new-stories.jade new file mode 100644 index 0000000000..e69de29bb2 diff --git a/views/stories/news-nav.jade b/views/stories/news-nav.jade new file mode 100644 index 0000000000..4307a16545 --- /dev/null +++ b/views/stories/news-nav.jade @@ -0,0 +1,15 @@ +.col-xs-12 + .btn-group.input-group.btn-group-justified(data-toggle='buttons') + label.btn.btn-primary.btn-big + input#option1(type='radio', name='options') + | Hot + label.btn.btn-primary.active.btn-big + input#option2(type='radio', name='options') + | New + label.btn.btn-primary.btn-big + input#option3(type='radio', name='options') + | Submit + label.btn.btn-primary.btn-big + input#option3(type='radio', name='options') + | Search + .spacer \ No newline at end of file diff --git a/views/stories/search-stories.jade b/views/stories/search-stories.jade new file mode 100644 index 0000000000..e69de29bb2 diff --git a/views/stories/submit.jade b/views/stories/submit-story.jade similarity index 97% rename from views/stories/submit.jade rename to views/stories/submit-story.jade index 9dacdc0e55..90ff56dcae 100644 --- a/views/stories/submit.jade +++ b/views/stories/submit-story.jade @@ -1,4 +1,4 @@ -.col-xs-12 +#story-submit.col-xs-12 form.form-horizontal.control-label-story-submission#story-submission-form .col-xs-2.img-story-post img.img-responsive(src='#{user.profile.picture}') From 5440efc7290490d2cdede55e06a2bd92cc93b713 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Fri, 6 Mar 2015 08:11:18 +0900 Subject: [PATCH 19/49] Wire hot, recent, submit, and search views in app and controller, modified button navigation to fetch their routes --- app.js | 49 +++++++++++++++++++++++++++------- controllers/story.js | 39 ++++++++++++++++++++++++--- views/partials/navbar.jade | 2 +- views/stories/hot-stories.jade | 2 +- views/stories/index.jade | 2 +- views/stories/new-stories.jade | 47 ++++++++++++++++++++++++++++++++ views/stories/news-nav.jade | 24 ++++++++++------- 7 files changed, 140 insertions(+), 25 deletions(-) diff --git a/app.js b/app.js index 237cd1f65a..358578f0a7 100644 --- a/app.js +++ b/app.js @@ -281,17 +281,20 @@ app.post( * Main routes. */ app.get( - '/stories/', - storyController.index -); -app.get( - '/stories/index', - storyController.json + '/stories/hotStories', + storyController.hotJSON ); -app.post( - '/stories/comment/submit', - storyController.commentSubmit +app.get( + '/stories/recentStories', + storyController.recentJSON +); + +app.get( + '/stories/', + function(req, res) { + res.redirect(302, '/stories/hot'); + } ); app.get( @@ -299,15 +302,43 @@ app.get( storyController.comments ); +app.post( + '/stories/comment/submit', + storyController.commentSubmit +); + +app.get( + '/stories/submit', + storyController.submitNew +); + app.post( '/stories/submit', storyController.storySubmission ); + +app.get( + '/stories/hot', + storyController.hot +); + +app.get( + '/stories/recent', + storyController.recent +); + + +app.get( + '/stories/search', + storyController.search +); + app.get( '/stories/:storyName', storyController.returnIndividualStory ); + app.post( '/stories/upvote/', storyController.upvote diff --git a/controllers/story.js b/controllers/story.js index 8ea25044ca..485c2bcd10 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -6,7 +6,7 @@ var R = require('ramda'), moment = require('../public/js/lib/moment/moment.js'), resources = require('./resources'); -exports.json = function(req, res, next) { +exports.hotJSON = function(req, res, next) { var story = Story.find({}).sort({'rank': -1}); story.exec(function(err, stories) { if (err) { @@ -16,13 +16,44 @@ exports.json = function(req, res, next) { }); }; -exports.index = function(req, res, next) { - var story = Story.find({}).sort({'rank': -1}); +exports.recentJSON = function(req, res, next) { + var story = Story.find({}).sort({'timePosted': -1}); story.exec(function(err, stories) { if (err) { throw err; } - res.render('stories/index'); + res.json(stories); + }); +}; + +exports.hot = function(req, res, next) { + res.render('stories/index', { + page: 'hot' + }); +}; + +exports.submitNew = function(req,res, next) { + res.render('stories/submit-story', { + page: 'submit' + }); +}; + +exports.search = function(req, res, next) { + res.render('stories/search-stories', { + page: 'search' + }); +}; + +exports.recent = function(req, res, next) { + res.render('stories/index', { + page: 'recent' + }); +}; + +exports.specificView = function(req, res, next) { + var whichView = req.params.type; + res.render('stories/index', { + page: whichView }); }; diff --git a/views/partials/navbar.jade b/views/partials/navbar.jade index 9c19b96b52..16fd86ce30 100644 --- a/views/partials/navbar.jade +++ b/views/partials/navbar.jade @@ -16,7 +16,7 @@ li a(href='/chat') Chat li - a(href='/stories') News + a(href='/stories/hot') News li a(href='/bonfires') Bonfires if !user diff --git a/views/stories/hot-stories.jade b/views/stories/hot-stories.jade index c3dcc96caa..7fb206cb0d 100644 --- a/views/stories/hot-stories.jade +++ b/views/stories/hot-stories.jade @@ -7,7 +7,7 @@ h3 return name.toLowerCase().replace(/\s/g, '-'); } $.ajax({ - url: '/stories/index', + url: '/stories/hotStories', type: 'GET' }) .success( diff --git a/views/stories/index.jade b/views/stories/index.jade index 609cd7f32a..796ce74fa4 100644 --- a/views/stories/index.jade +++ b/views/stories/index.jade @@ -9,7 +9,7 @@ block content include ./news-nav if (page === 'hot') include ./hot-stories - if (page === 'new') + if (page === 'recent') include ./new-stories if (page === 'submit') include ./submit-story diff --git a/views/stories/new-stories.jade b/views/stories/new-stories.jade index e69de29bb2..6378303a5f 100644 --- a/views/stories/new-stories.jade +++ b/views/stories/new-stories.jade @@ -0,0 +1,47 @@ +h3 + ul#story-list.story-list + + script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") + script. + var getLinkedName = function getLinkedName(name) { + return name.toLowerCase().replace(/\s/g, '-'); + } + $.ajax({ + url: '/stories/recentStories', + type: 'GET' + }) + .success( + function(data) { + for (var i = 0; i < data.length; i++) { + var li = document.createElement('li'); + var linkedName = getLinkedName(data[i].storyLink); + var rank = data[i].rank; + + $(li).html("
" + + "
" + + "
" + + "" + rank + "" + + "
" + + "
" + + "
" + + "" + + "
" + + "
" + + "
" + + "" + + "
" + + "Posted " + + moment(data[i].timePosted).fromNow() + + " by @" + data[i].author.username + " " + + "
" + + "
" + + "
" + + "
" + + ""); + $(li).appendTo($('#story-list')); + } + }); \ No newline at end of file diff --git a/views/stories/news-nav.jade b/views/stories/news-nav.jade index 4307a16545..058fb406df 100644 --- a/views/stories/news-nav.jade +++ b/views/stories/news-nav.jade @@ -1,15 +1,21 @@ .col-xs-12 .btn-group.input-group.btn-group-justified(data-toggle='buttons') - label.btn.btn-primary.btn-big - input#option1(type='radio', name='options') + label.btn.btn-primary.btn-big(class="#{ page === 'hot' ? 'active' : '' }") + input#option1(href='/stories/hot', type='radio', name='options') | Hot - label.btn.btn-primary.active.btn-big - input#option2(type='radio', name='options') + label.btn.btn-primary.btn-big(class="#{ page === 'recent' ? 'active' : '' }") + input#option2(href='/stories/recent', type='radio', name='options') | New - label.btn.btn-primary.btn-big - input#option3(type='radio', name='options') + label.btn.btn-primary.btn-big(class="#{ page === 'submit' ? 'active' : '' }") + input#option3(href='/stories/submit', type='radio', name='options') | Submit - label.btn.btn-primary.btn-big - input#option3(type='radio', name='options') + label.btn.btn-primary.btn-big(class="#{ page === 'search' ? 'active' : '' }") + input#option4(href='/stories/search', type='radio', name='options') | Search - .spacer \ No newline at end of file + script. + $('label').on('click', function() { + console.log('clicked'); + window.location = ($(this).children('input').attr('href')); + }); + +.spacer \ No newline at end of file From 0276c6867e2062671151be6312b34f669344e424 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Fri, 6 Mar 2015 08:20:49 +0900 Subject: [PATCH 20/49] Display hot/recent/submit/search views correctly through index --- controllers/story.js | 4 ++-- views/stories/index.jade | 1 + views/stories/submit-story.jade | 7 ++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/controllers/story.js b/controllers/story.js index 485c2bcd10..98c7c2a1bd 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -33,13 +33,13 @@ exports.hot = function(req, res, next) { }; exports.submitNew = function(req,res, next) { - res.render('stories/submit-story', { + res.render('stories/index', { page: 'submit' }); }; exports.search = function(req, res, next) { - res.render('stories/search-stories', { + res.render('stories/index', { page: 'search' }); }; diff --git a/views/stories/index.jade b/views/stories/index.jade index 796ce74fa4..b42ff06b5a 100644 --- a/views/stories/index.jade +++ b/views/stories/index.jade @@ -7,6 +7,7 @@ block content .panel-heading.text-center Camper News .panel-body include ./news-nav + .spacer if (page === 'hot') include ./hot-stories if (page === 'recent') diff --git a/views/stories/submit-story.jade b/views/stories/submit-story.jade index 90ff56dcae..8b7ea0837f 100644 --- a/views/stories/submit-story.jade +++ b/views/stories/submit-story.jade @@ -1,4 +1,5 @@ -#story-submit.col-xs-12 +.spacer +.col-xs-12 form.form-horizontal.control-label-story-submission#story-submission-form .col-xs-2.img-story-post img.img-responsive(src='#{user.profile.picture}') @@ -13,6 +14,6 @@ label.control-label.control-label-story-submission(for='name') Title .col-xs-11 input.form-control(placeholder='Type a headline for your link here', name='Title') + .spacer .form-group - .btn.btn-large.btn-block.btn-primary#story-submit Submit -.spacer \ No newline at end of file + .btn.btn-big.btn-block.btn-primary#story-submit Submit \ No newline at end of file From 6fc72de232c0e9238dff32c308f2dfbcd1bc8d28 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Fri, 6 Mar 2015 09:20:30 +0900 Subject: [PATCH 21/49] add basic text search for post headlines --- app.js | 5 +++ controllers/story.js | 24 ++++++++----- public/js/main.js | 8 +++-- views/stories/news-nav.jade | 1 - views/stories/search-stories.jade | 58 +++++++++++++++++++++++++++++++ views/stories/submit-story.jade | 2 +- 6 files changed, 85 insertions(+), 13 deletions(-) diff --git a/app.js b/app.js index 358578f0a7..3219020e64 100644 --- a/app.js +++ b/app.js @@ -334,6 +334,11 @@ app.get( storyController.search ); +app.post( + '/stories/search', + storyController.getStories +); + app.get( '/stories/:storyName', storyController.returnIndividualStory diff --git a/controllers/story.js b/controllers/story.js index 98c7c2a1bd..3fb24bfe8f 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -50,12 +50,6 @@ exports.recent = function(req, res, next) { }); }; -exports.specificView = function(req, res, next) { - var whichView = req.params.type; - res.render('stories/index', { - page: whichView - }); -}; exports.returnIndividualStory = function(req, res, next) { var dashedName = req.params.storyName; @@ -90,7 +84,7 @@ exports.returnIndividualStory = function(req, res, next) { link: story.link, author: story.author, description: story.description, - rank: story.rank, + rank: story.upVotes.length, upVotes: story.upVotes, comments: story.comments, id: story._id, @@ -101,6 +95,18 @@ exports.returnIndividualStory = function(req, res, next) { }); }; +exports.getStories = function(req, res, next) { + debug('this is data', req.body.data.searchValue); + Story.find({'headline': new RegExp(req.body.data.searchValue, 'i')}, function(err, results) { + if (err) { + res.status(500); + } + debug('results are', results); + + res.json(results); + }); +}; + exports.upvote = function(req, res, next) { var data = req.body.data; Story.find({'_id': data.id}, function(err, story) { @@ -149,8 +155,8 @@ exports.storySubmission = function(req, res, next) { timePosted: Date.now(), link: link, description: data.description, - rank: 0, - upVotes: 0, + rank: 1, + upVotes: data.upVotes, author: data.author, comments: [], image: data.image, diff --git a/public/js/main.js b/public/js/main.js index da5163c3e3..6d50db7742 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -153,6 +153,10 @@ $(document).ready(function() { var data = $('#story-submission-form :input'); var link = $(data[0]).val(); var headline = $(data[1]).val(); + var userDataForUpvote = { + upVotedBy: user._id, + upVotedByUsername: user.profile.username + }; $('#story-submit').unbind('click'); $.post('/stories/submit', { @@ -162,8 +166,8 @@ $(document).ready(function() { timePosted: Date.now(), description: 'TODO', - rank: 0, - upVotes: 0, + rank: 1, + upVotes: [userDataForUpvote], author: { picture: user.profile.picture, userId: user._id, diff --git a/views/stories/news-nav.jade b/views/stories/news-nav.jade index 058fb406df..dd8e3e90af 100644 --- a/views/stories/news-nav.jade +++ b/views/stories/news-nav.jade @@ -14,7 +14,6 @@ | Search script. $('label').on('click', function() { - console.log('clicked'); window.location = ($(this).children('input').attr('href')); }); diff --git a/views/stories/search-stories.jade b/views/stories/search-stories.jade index e69de29bb2..e9c59fb361 100644 --- a/views/stories/search-stories.jade +++ b/views/stories/search-stories.jade @@ -0,0 +1,58 @@ +input#searchArea(type=text) +button#searchbutton +#story-list + ul#stories +script. + $('#searchbutton').on('click', function() { + $('#stories').empty(); + var searchTerm = $('#searchArea').val(); + var getLinkedName = function getLinkedName(name) { + return name.toLowerCase().replace(/\s/g, '-'); + } + $.post('/stories/search', + { + data: { + searchValue: searchTerm + } + }) + .fail(function (xhr, textStatus, errorThrown) { + console.log('failure'); + //$('#comment-button').bind('click', commentSubmitButtonHandler); + }) + .done(function (data, textStatus, xhr) { + console.log(data); + for (var i = 0; i < data.length; i++) { + var li = document.createElement('li'); + var linkedName = getLinkedName(data[i].storyLink); + var rank = data[i].rank; + + $(li).html( + "
" + + "
" + + "
" + + "" + rank + "" + + "
" + + "
" + + "
" + + "" + + "
" + + "
" + + "
" + + "" + + "
" + + "Posted " + + moment(data[i].timePosted).fromNow() + + " by @" + data[i].author.username + " " + + "
" + + "
" + + "
" + + "
" + + ""); + $(li).appendTo($('#stories')); + } + }); + }); \ No newline at end of file diff --git a/views/stories/submit-story.jade b/views/stories/submit-story.jade index 8b7ea0837f..a1bba32ae6 100644 --- a/views/stories/submit-story.jade +++ b/views/stories/submit-story.jade @@ -16,4 +16,4 @@ input.form-control(placeholder='Type a headline for your link here', name='Title') .spacer .form-group - .btn.btn-big.btn-block.btn-primary#story-submit Submit \ No newline at end of file + .btn.btn-big.btn-block.btn-primary#story-submit Submit From 9df29350ed59af86790f76d6ef89266aac283698 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Thu, 5 Mar 2015 17:30:05 -0800 Subject: [PATCH 22/49] update the search UI --- public/css/main.less | 5 +++ views/stories/index.jade | 2 + views/stories/search-stories.jade | 72 +++++++++++++++++++------------ 3 files changed, 51 insertions(+), 28 deletions(-) diff --git a/public/css/main.less b/public/css/main.less index 55dbdd2332..49b1d829d3 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -387,6 +387,11 @@ ul { font-size: 30px; } +.big-text-field { + font-size: 30px; + height: 57px; +} + .table { margin-left: -16px; } diff --git a/views/stories/index.jade b/views/stories/index.jade index b42ff06b5a..9e71245a01 100644 --- a/views/stories/index.jade +++ b/views/stories/index.jade @@ -1,8 +1,10 @@ extends ../layout block content + script(src='/js/lib/moment/moment.js') script. var challengeName = 'Camper News'; var user = !{JSON.stringify(user)}; + var page = !{JSON.stringify(page)}; .panel.panel-info .panel-heading.text-center Camper News .panel-body diff --git a/views/stories/search-stories.jade b/views/stories/search-stories.jade index e9c59fb361..19926f8be0 100644 --- a/views/stories/search-stories.jade +++ b/views/stories/search-stories.jade @@ -1,10 +1,27 @@ -input#searchArea(type=text) -button#searchbutton +.col-xs-12 + .spacer + .input-group + input#searchArea.big-text-field.form-control(type='text', placeholder='Search for...', autofocus) + span.input-group-btn + button#searchbutton.btn.btn-big.btn-primary(type='button') Search + .spacer + #story-list ul#stories script. + $('#searchArea').keypress(function(event) { + if (event.keyCode === 13 || event.which === 13) { + executeSearch(); + $('#searchArea').focus(); + event.preventDefault(); + } + }); $('#searchbutton').on('click', function() { + executeSearch(); + }); + function executeSearch() { $('#stories').empty(); + console.log('clicked or enter button'); var searchTerm = $('#searchArea').val(); var getLinkedName = function getLinkedName(name) { return name.toLowerCase().replace(/\s/g, '-'); @@ -17,10 +34,8 @@ script. }) .fail(function (xhr, textStatus, errorThrown) { console.log('failure'); - //$('#comment-button').bind('click', commentSubmitButtonHandler); }) .done(function (data, textStatus, xhr) { - console.log(data); for (var i = 0; i < data.length; i++) { var li = document.createElement('li'); var linkedName = getLinkedName(data[i].storyLink); @@ -28,31 +43,32 @@ script. $(li).html( "
" + - "
" + - "
" + - "" + rank + "" + + "
" + + "
" + + "" + rank + "" + + "
" + + "
" + + "
" + + "" + + "
" + + "
" + + "
" + + "" + + "
" + + "Posted " + + moment(data[i].timePosted).fromNow() + + " by @" + data[i].author.username + " " + + "
" + + "
" + + "
" + "
" + - "
" + - "
" + - "" + - "
" + - "
" + - "
" + - "" + - "
" + - "Posted " + - moment(data[i].timePosted).fromNow() + - " by @" + data[i].author.username + " " + - "
" + - "
" + - "
" + - "
" + - ""); + ""); $(li).appendTo($('#stories')); } }); - }); \ No newline at end of file + + } \ No newline at end of file From aad15b88baaa593bb450971e2a5d751f52145295 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Fri, 6 Mar 2015 17:25:28 +0900 Subject: [PATCH 23/49] fudging around with interpreting opengraph info in resources.js --- app.js | 5 +++++ controllers/resources.js | 31 +++++++++++++++++++++++++++---- package.json | 1 + 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index 3219020e64..ecce6a19e6 100644 --- a/app.js +++ b/app.js @@ -349,6 +349,11 @@ app.post( storyController.upvote ); +app.post( + '/stories/getURLMetaData', + resourcesController.getMetaData +); + /** * Challenge related routes */ diff --git a/controllers/resources.js b/controllers/resources.js index 54d566daf6..05d97b0939 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -8,7 +8,8 @@ var User = require('../models/User'), coursewares = require('../seed_data/coursewares.json'); Client = require('node-rest-client').Client, client = new Client(), - debug = require('debug')('freecc:cntr:bonfires'); + debug = require('debug')('freecc:cntr:bonfires'), + cheerio = require('cheerio'); /** * GET / @@ -119,13 +120,13 @@ module.exports = { }); }, trelloCalls: function(req, res) { - client.get('https://trello.com/1/boards/BA3xVpz9/cards?key=' + secrets.trello.key, function(trello, res2) { + client.get('https://trello.com/1/boards/BA3xVpz9/cards?key=' + secrets.trello.key, function(trello) { trello = trello ? (JSON.parse(trello)).length : "Can't connect to to Trello"; res.send({"trello": trello}); }); }, bloggerCalls: function(req, res) { - client.get('https://www.googleapis.com/blogger/v3/blogs/2421288658305323950/posts?key=' + secrets.blogger.key, function (blog, res5) { + client.get('https://www.googleapis.com/blogger/v3/blogs/2421288658305323950/posts?key=' + secrets.blogger.key, function (blog) { var blog = blog.length > 100 ? JSON.parse(blog) : ""; res.send({ blog1Title: blog ? blog["items"][0]["title"] : "Can't connect to Blogger", @@ -251,8 +252,30 @@ module.exports = { }, whichEnvironment: function() { return process.env.NODE_ENV; - } + }, + getMetaData: function(req, res, next) { + var url = req.body.data.url; + var result = {}; + client.get(url, function(siteInfo) { + var $ = cheerio.load(siteInfo); + + var meta = $('meta'); + $(meta, this).each(function () { + var prop = $(this).attr("property"), key, value; + if (prop && prop.substring(0, ns.length) === ns) { + key = prop.substring(ns.length); + value = $(this).attr("content"); + console.log("Found OGP data %s=%s", key, value); + result[key] = result[key] || []; + result[key].push(value); + } + }); + + res.json(JSON.stringify(result)); + + }); + } }; diff --git a/package.json b/package.json index 2e0986284d..94bd29bcd1 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "method-override": "^2.3.0", "moment": "^2.8.4", "mongoose": "^3.8.19", + "mongoose-text-search": "0.0.2", "morgan": "^1.5.0", "newrelic": "^1.13.3", "node": "0.0.0", From 53598a8e6fa32c0d3d85bf6b21a505a4183b6964 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Fri, 6 Mar 2015 18:47:41 +0900 Subject: [PATCH 24/49] Properly binding comment comment button to the proper comment --- views/stories/comments.jade | 78 ++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/views/stories/comments.jade b/views/stories/comments.jade index 2a2e5a8d55..3448323035 100644 --- a/views/stories/comments.jade +++ b/views/stories/comments.jade @@ -1,9 +1,10 @@ -h3.text-left +p.text-left div#comment-list.comment-list script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") script(src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/moment.min.js") script. + var sentinel = 0; var commentDetails; R.forEach(function displayComments(comment) { console.log('this is the comment id', comment); @@ -22,42 +23,63 @@ h3.text-left commentDetails = data; var div = document.createElement('div'); $(div) - .html("" + - "
" + - "
" + - "
" + - "
" + - "" + + .html("

" + + "

" + + "
" + + "
" + + "
" + + "

" + + commentDetails.rank + + "

" + + "
" + "
" + "
" + - "
" + - "
" + - commentDetails.rank + + "
" + + "" + + "
" + + "
" + + "
" + + "
" + + commentDetails.body + + "
" + + "
" + + "
" + + "
" + + "
" + + "commented " + moment(commentDetails.commentedOn).fromNow() + " by " + + "@" + commentDetails.author.username + "" + + "
" + + "
" + + "
" + + "
" + + "
" + + "" + + "
" + "
" + "
" + "
" + - "
" + - "" + - "
" + - "
" + - "
" + - "
" + - commentDetails.body + - "
" + - "
" + - "
" + - "
" + - "commented " + moment(commentDetails.commentedOn).fromNow() + " by " + - "@" + commentDetails.author.username + "" + - "
" + - "
" + - "
" + - "
" + + "

" + "
") .appendTo($('#comment-list')); + + }, + complete: function() { + sentinel++; + console.log(sentinel, comments.length); + if (sentinel < comments.length) { + return; + } + $('.comment-a-comment').on('click', 'button', function() { + console.log('clicked'); + console.log($(this)); + }); } }) - }, comments); \ No newline at end of file + + }, comments); + + + From 6910ed0aea2cdffbe98e0c2b2cc3058fc65d80e3 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 7 Mar 2015 01:57:09 +0900 Subject: [PATCH 25/49] Nesting of comments in view, disabling comment form display button on button click --- app.js | 9 +- controllers/story.js | 59 ++++++++---- models/Comment.js | 4 + public/js/main.js | 4 +- seed_data/comments.json | 3 +- views/stories/comments.jade | 183 ++++++++++++++++++++++-------------- 6 files changed, 168 insertions(+), 94 deletions(-) diff --git a/app.js b/app.js index ecce6a19e6..6e5fefd8db 100644 --- a/app.js +++ b/app.js @@ -303,17 +303,22 @@ app.get( ); app.post( - '/stories/comment/submit', + '/stories/comment/', storyController.commentSubmit ); +app.post( + '/stories/comment/:id/comment', + storyController.commentOnCommentSubmit +); + app.get( '/stories/submit', storyController.submitNew ); app.post( - '/stories/submit', + '/stories/', storyController.storySubmission ); diff --git a/controllers/story.js b/controllers/story.js index 3fb24bfe8f..4c4fad5dbb 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -183,28 +183,53 @@ exports.commentSubmit = function(req, res, next) { rank: 0, upvotes: 0, author: data.author, - comments: [] + comments: [], + topLevel: true }); + commentSave(comment, Story, res); +}; +exports.commentOnCommentSubmit = function(req, res, next) { + var idToFind = req.params.id; + debug('idToFind', idToFind); + var data = req.body.data; + var comment = new Comment({ + associatedPost: data.associatedPost, + body: data.body, + rank: 0, + upvotes: 0, + author: data.author, + comments: [], + topLevel: false + }); + commentSave(comment, Comment, res); +}; + +function commentSave(comment, Context, res) { comment.save(function(err, data) { if (err) { return res.status(500); } debug('this is data from save', data); - Story.find({'_id': comment.associatedPost}, function(err, associatedStory) { - if (err) { - return res.status(500); - } - associatedStory = associatedStory.pop(); - if (associatedStory) { - associatedStory.comments.push(data._id); - associatedStory.save(function(err, data) { - if (err) { - res.status(500); - } - res.send(true); - }); - } - }); + try { + Context.find({'_id': comment.associatedPost}, function (err, associatedStory) { + if (err) { + return res.status(500); + } + associatedStory = associatedStory.pop(); + if (associatedStory) { + associatedStory.comments.push(data._id); + associatedStory.save(function (err, data) { + if (err) { + res.status(500); + } + res.send(true); + }); + } + }); + } catch (e) { + // delete comment + return res.status(500); + } }); -}; +} diff --git a/models/Comment.js b/models/Comment.js index 9206b6ed75..8af4aaefcf 100644 --- a/models/Comment.js +++ b/models/Comment.js @@ -22,6 +22,10 @@ var commentSchema = new mongoose.Schema({ comments: { type: Array, default: [] + }, + toplevel: { + type: Boolean, + default: false } }); diff --git a/public/js/main.js b/public/js/main.js index 6d50db7742..04e8ee4769 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -158,7 +158,7 @@ $(document).ready(function() { upVotedByUsername: user.profile.username }; $('#story-submit').unbind('click'); - $.post('/stories/submit', + $.post('/stories/', { data: { link: link, @@ -192,7 +192,7 @@ $(document).ready(function() { var data = $('#comment-box').val(); $('#comment-button').unbind('click'); - $.post('/stories/comment/submit', + $.post('/stories/comment/', { data: { associatedPost: storyId, diff --git a/seed_data/comments.json b/seed_data/comments.json index 5f4b1efc37..11fa35aa87 100644 --- a/seed_data/comments.json +++ b/seed_data/comments.json @@ -9,6 +9,7 @@ "picture": "https://thedrinkingtraveler.files.wordpress.com/2015/02/24127-funny-grumpy-cat-memesvery-bad-morning-meme-0rlh4r5c-wallpaper-1024x1024.jpg", "id" : "ade671aa0756dc61bd485f45" }, - "comments": [] + "comments": [], + "topLevel": true } ] \ No newline at end of file diff --git a/views/stories/comments.jade b/views/stories/comments.jade index 3448323035..d8523b8fa2 100644 --- a/views/stories/comments.jade +++ b/views/stories/comments.jade @@ -1,85 +1,124 @@ -p.text-left +.text-left div#comment-list.comment-list script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") script(src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/moment.min.js") script. var sentinel = 0; - var commentDetails; - R.forEach(function displayComments(comment) { - console.log('this is the comment id', comment); - $.ajax({ - type: 'GET', - url: '/stories/comments/' + comment, - error: function(xhr, textStatus, errorThrown) { - console.log('got error'); - commentDetails = { - error: true, - body: 'There seems to be a problem fetching this comment.' + var renderComments = function renderComments(comments, containerSelector, level) { + var commentDetails; + var offSetClass = level ? 'col-xs-offset-' + level : ''; + var rowClass = level ? 'col-xs-' + (12 - level) : 'col-xs-12'; + R.forEach(function displayComments(comment) { + $.ajax({ + type: 'GET', + url: '/stories/comments/' + comment, + error: function (xhr, textStatus, errorThrown) { + console.log('got error'); + commentDetails = { + error: true, + body: 'There seems to be a problem fetching this comment.' + } + }, + success: function (data, textStatus, xhr) { + commentDetails = data; + var div = document.createElement('div'); + $(div) + .html( + "
" + + "
" + + "
" + + "

" + + commentDetails.rank + + "

" + + "
" + + "
" + + "
" + + "
" + + "" + + "
" + + "
" + + "
" + + "
" + + commentDetails.body + + "
" + + "
" + + "
" + + "
" + + "
" + + "commented " + moment(commentDetails.commentedOn).fromNow() + " by " + + "@" + commentDetails.author.username + "" + + "
" + + "
" + + "
" + + "
" + + "
" + + "" + + "
" + + "
" + + "
" + ) + .addClass('comment row ' + offSetClass + ' ' + rowClass + ' comment_' + commentDetails._id) + .appendTo($(containerSelector)); + + sentinel += commentDetails.comments.length; + + renderComments(commentDetails.comments, '.comment_' + commentDetails._id, ++level); + + }, + complete: function () { + sentinel--; + if (!sentinel) { + console.log('Binding click handler'); + $('.comment-a-comment').on('click', 'button', function () { + $(this).attr('disabled', 'disabled'); + console.log('Unbinding click handler'); + + var div = document.createElement('div'); + var commentId = $(this).attr('id'); + $(div).html( + "" + + "" + ) + .addClass('col-xs-6 col-xs-offset-3') + .appendTo($(this).closest('.comment')); + var submitCommentToCommentHandler = function submitCommentToCommentHandler() { + $('#submit-comment-to-comment').unbind('click'); + $.post('/stories/comment/' + commentId + '/comment', + { + data: { + associatedPost: commentId, + body: $('#comment-to-comment-textarea').val(), + author: { + picture: user.profile.picture, + userId: user._id, + username: user.profile.username + } + } + }) + .fail(function (xhr, textStatus, errorThrown) { + $('#submit-comment-to-comment').bind('click', submitCommentToCommentHandler); + }) + .done(function (data, textStatus, xhr) { + //window.location = '/stories/' + JSON.parse(data).storyLink; + }); + + }; + + $('#submit-comment-to-comment').on('click', submitCommentToCommentHandler); + });// + } } - }, - success: function (data, textStatus, xhr) { - console.log(data); - commentDetails = data; - var div = document.createElement('div'); - $(div) - .html("

" + - "

" + - "
" + - "
" + - "
" + - "

" + - commentDetails.rank + - "

" + - "
" + - "
" + - "
" + - "
" + - "" + - "
" + - "
" + - "
" + - "
" + - commentDetails.body + - "
" + - "
" + - "
" + - "
" + - "
" + - "commented " + moment(commentDetails.commentedOn).fromNow() + " by " + - "@" + commentDetails.author.username + "" + - "
" + - "
" + - "
" + - "
" + - "
" + - "" + - "
" + - "
" + - "
" + - "
" + - "

" + - "
") - .appendTo($('#comment-list')); - - }, - complete: function() { - sentinel++; - console.log(sentinel, comments.length); - if (sentinel < comments.length) { - return; - } - $('.comment-a-comment').on('click', 'button', function() { - console.log('clicked'); - console.log($(this)); - }); - } + }) - }) + }, comments); + } + sentinel += comments.length; + + renderComments(comments, $('#comment-list'), 0); + - - }, comments); From b46fb576c38c2d2717747fc49d075aac681ccb1f Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 7 Mar 2015 02:24:28 +0900 Subject: [PATCH 26/49] window refreshes on comment submission --- public/js/main.js | 2 +- views/stories/comments.jade | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/public/js/main.js b/public/js/main.js index 04e8ee4769..fa3d6e68f3 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -208,7 +208,7 @@ $(document).ready(function() { $('#comment-button').bind('click', commentSubmitButtonHandler); }) .done(function (data, textStatus, xhr) { - //window.location = '/stories/' + JSON.parse(data).storyLink; + window.location.reload(); }); }; diff --git a/views/stories/comments.jade b/views/stories/comments.jade index d8523b8fa2..cda4f4f62d 100644 --- a/views/stories/comments.jade +++ b/views/stories/comments.jade @@ -25,6 +25,7 @@ var div = document.createElement('div'); $(div) .html( + "
" + "
" + "
" + "
" + @@ -56,9 +57,10 @@ "" + "
" + "
" + + "
" + "
" ) - .addClass('comment row ' + offSetClass + ' ' + rowClass + ' comment_' + commentDetails._id) + .addClass(offSetClass + ' row ' + rowClass + ' comment_' + commentDetails._id) .appendTo($(containerSelector)); sentinel += commentDetails.comments.length; @@ -100,7 +102,7 @@ $('#submit-comment-to-comment').bind('click', submitCommentToCommentHandler); }) .done(function (data, textStatus, xhr) { - //window.location = '/stories/' + JSON.parse(data).storyLink; + window.location.reload(); }); }; From be32cd51b833219c566ed17343d6e30f3ede35de Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 7 Mar 2015 03:06:38 +0900 Subject: [PATCH 27/49] Added text-index searching --- controllers/story.js | 55 ++++++++++++++++++++++++++++++++++++-------- package.json | 1 + 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/controllers/story.js b/controllers/story.js index 4c4fad5dbb..394ece6300 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -4,7 +4,10 @@ var R = require('ramda'), Comment = require('./../models/Comment'), User = require('./../models/User'), moment = require('../public/js/lib/moment/moment.js'), - resources = require('./resources'); + resources = require('./resources'), + mongodb = require('mongodb'), + MongoClient = mongodb.MongoClient, + secrets = require('../config/secrets'); exports.hotJSON = function(req, res, next) { var story = Story.find({}).sort({'rank': -1}); @@ -96,15 +99,47 @@ exports.returnIndividualStory = function(req, res, next) { }; exports.getStories = function(req, res, next) { - debug('this is data', req.body.data.searchValue); - Story.find({'headline': new RegExp(req.body.data.searchValue, 'i')}, function(err, results) { - if (err) { - res.status(500); - } - debug('results are', results); - - res.json(results); - }); + MongoClient.connect(secrets.db, function(err, database) { + db = database; + debug('this is data', req.body.data.searchValue); + db.collection('stories').find({ + "$text": { + "$search": req.body.data.searchValue + } + }, { + headline: 1, + timePosted: 1, + link: 1, + description: 1, + rank: 1, + upVotes: 1, + author: 1, + comments: 1, + image: 1, + storyLink: 1, + textScore: { + $meta: "textScore" + } + }, { + sort: { + textScore: { + $meta: "textScore" + } + } + }).toArray(function(err, items) { + debug('items', items); + return res.json(items); + }); + }); + //Story.find({'headline': new RegExp(req.body.data.searchValue, 'i')}, function (err, results) { + // if (err) { + // res.status(500); + // } + // debug('results are', results); + // + // res.json(results); + //}); + //}a); }; exports.upvote = function(req, res, next) { diff --git a/package.json b/package.json index 94bd29bcd1..5ef6868d00 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "lusca": "^1.0.2", "method-override": "^2.3.0", "moment": "^2.8.4", + "mongodb": "^1.4.33", "mongoose": "^3.8.19", "mongoose-text-search": "0.0.2", "morgan": "^1.5.0", From 89dd38cbac7578c76e061db3023f1cbe2bcf3b4b Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 7 Mar 2015 03:55:59 +0900 Subject: [PATCH 28/49] Fix minor bugs --- controllers/story.js | 2 +- seed_data/comments.json | 14 +----- seed_data/stories.json | 83 +--------------------------------- views/stories/hot-stories.jade | 2 +- 4 files changed, 4 insertions(+), 97 deletions(-) diff --git a/controllers/story.js b/controllers/story.js index 394ece6300..5c32741461 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -76,7 +76,7 @@ exports.returnIndividualStory = function(req, res, next) { } story = story.pop(); - var dashedNameFull = story.headline.toLowerCase().replace(/\s/g, '-'); + var dashedNameFull = story.storyLink.toLowerCase().replace(/\s/g, '-'); if (dashedNameFull !== dashedName) { return res.redirect('../stories/' + dashedNameFull); } diff --git a/seed_data/comments.json b/seed_data/comments.json index 11fa35aa87..c44dc44f37 100644 --- a/seed_data/comments.json +++ b/seed_data/comments.json @@ -1,15 +1,3 @@ [ - { - "associatedPost": "54f584db16cd6570aa176eda", - "body": "cats like to sit around and be lazy as hell", - "rank": "1", - "upvotes": [], - "author": { - "username" : "terakilonoob", - "picture": "https://thedrinkingtraveler.files.wordpress.com/2015/02/24127-funny-grumpy-cat-memesvery-bad-morning-meme-0rlh4r5c-wallpaper-1024x1024.jpg", - "id" : "ade671aa0756dc61bd485f45" - }, - "comments": [], - "topLevel": true - } + ] \ No newline at end of file diff --git a/seed_data/stories.json b/seed_data/stories.json index 246950ec44..c44dc44f37 100644 --- a/seed_data/stories.json +++ b/seed_data/stories.json @@ -1,84 +1,3 @@ [ - { - "headline": "Cat sits on keyboard 0", - "storyLink": "cat sits on keyboard 0", - "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", - "description": "cats love keyboards you know", - "rank": 0, - "upVotes": [], - "timePosted": 1425429660000, - "author": { - "username": "testuser", - "userId": "a2ad135e2aa27c14fc73ee11", - "picture": "http://www.cutecatgifs.com/wp-content/uploads/2013/11/little_guy_sleeping_in_lap.gif" - }, - "comments": [], - "image": "http://rossmounce.co.uk/wp-content/uploads/2014/11/grumpy-cat-no-1.jpg" -}, - { - "headline": "Cat sits on keyboard 1", - "storyLink": "cat sits on keyboard 1", - "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", - "description": "cats love keyboards you know", - "rank": 1, - "upVotes": [], - "timePosted": 1425429660000, - "author": { - "username": "testuser", - "userId": "a2ad135e2aa27c14fc73ee22", - "picture": "http://www.cutecatgifs.com/wp-content/uploads/2013/11/little_guy_sleeping_in_lap.gif" - }, - "comments": [], - "image": "http://rossmounce.co.uk/wp-content/uploads/2014/11/grumpy-cat-no-1.jpg" -}, - { - "headline": "Cat sits on keyboard 3", - "storyLink": "cat sits on keyboard 3", - "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", - "description": "cats love keyboards you know", - "rank": 3, - "upVotes": [], - "timePosted": 1425429660000, - "author": { - "username": "testuser", - "userId": "a2ad135e2aa27c14fc73ee33", - "picture": "http://www.cutecatgifs.com/wp-content/uploads/2013/11/little_guy_sleeping_in_lap.gif" - }, - "comments": [], - "image": "http://rossmounce.co.uk/wp-content/uploads/2014/11/grumpy-cat-no-1.jpg" -}, - { - "headline": "Cat sits on keyboard 20", - "storyLink": "cat sits on keyboard 20", - "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", - "description": "ipsizzle dolizzle sit amet, ghetto adipiscing elit. Nullam fo shizzle velizzle, aliquet volutpizzle, suscipizzle shiz, gravida vizzle, arcu. Pellentesque izzle tortor. Sizzle doggy. Boom shackalack izzle dolizzle dapibizzle ass tempizzle tellivizzle. Ma nizzle we gonna chung shiz izzle tellivizzle. Vestibulum dizzle tortor. Pellentesque pimpin' rhoncus you son of a bizzle. In dizzle habitasse platea dictumst. Donec dapibizzle. Curabitizzle pot yippiyo, pretizzle da bomb, mattis izzle, da bomb vitae, nunc. Ass suscipizzle. Cool sempizzle bow wow wow sed purus.", - "rank": 20, - "upVotes": [], - "timePosted": 1425429660000, - "author": { - "username": "testuser", - "userId": "a2ad135e2aa27c14fc73ee44", - "picture": "http://www.cutecatgifs.com/wp-content/uploads/2013/11/little_guy_sleeping_in_lap.gif" - }, - "comments": [ - "54f61b0e43f0c2b90f162ec4" - ], - "image": "http://rossmounce.co.uk/wp-content/uploads/2014/11/grumpy-cat-no-1.jpg" -}, - { - "headline": "Cat sits on keyboard 2", - "storyLink": "cat sits on keyboard 2", - "link": "http://kotaku.com/5991046/why-cats-love-sitting-on-keyboards", - "description": "cats love keyboards you know", - "rank": 2, - "upVotes": [], - "timePosted": 1425429660000, - "author": { - "username": "testuser", - "userId": "a2ad135e2aa27c14fc73ee55", - "picture": "http://www.cutecatgifs.com/wp-content/uploads/2013/11/little_guy_sleeping_in_lap.gif" - }, - "comments": [], - "image": "http://rossmounce.co.uk/wp-content/uploads/2014/11/grumpy-cat-no-1.jpg" - } + ] \ No newline at end of file diff --git a/views/stories/hot-stories.jade b/views/stories/hot-stories.jade index 7fb206cb0d..c30cd444ab 100644 --- a/views/stories/hot-stories.jade +++ b/views/stories/hot-stories.jade @@ -4,7 +4,7 @@ h3 script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") script. var getLinkedName = function getLinkedName(name) { - return name.toLowerCase().replace(/\s/g, '-'); + return name.trim().toLowerCase().replace(/\s/g, '-'); } $.ajax({ url: '/stories/hotStories', From 76842ee7137eb9b3aa69573fa266b1e794eacf45 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 7 Mar 2015 04:51:00 +0900 Subject: [PATCH 29/49] Respond with 404 if no query found ref #151 --- controllers/story.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/controllers/story.js b/controllers/story.js index 5c32741461..3f2ea24bcc 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -100,7 +100,7 @@ exports.returnIndividualStory = function(req, res, next) { exports.getStories = function(req, res, next) { MongoClient.connect(secrets.db, function(err, database) { - db = database; + var db = database; debug('this is data', req.body.data.searchValue); db.collection('stories').find({ "$text": { @@ -127,19 +127,13 @@ exports.getStories = function(req, res, next) { } } }).toArray(function(err, items) { - debug('items', items); - return res.json(items); + if (items.length !== 0) { + debug('items found with full text', items); + return res.json(items); + } + return res.status(404); }); }); - //Story.find({'headline': new RegExp(req.body.data.searchValue, 'i')}, function (err, results) { - // if (err) { - // res.status(500); - // } - // debug('results are', results); - // - // res.json(results); - //}); - //}a); }; exports.upvote = function(req, res, next) { From b655cb7532e842718317c4fe3f15a50ce216440e Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 7 Mar 2015 16:08:15 +0900 Subject: [PATCH 30/49] Title scraping in resource.js getURLTitle function --- controllers/resources.js | 34 +++++++++++++--------------------- package.json | 2 +- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/controllers/resources.js b/controllers/resources.js index 5f5b15ae9b..b2f271e627 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -10,8 +10,9 @@ var User = require('../models/User'), moment = require('moment'), Client = require('node-rest-client').Client, client = new Client(), - debug = require('debug')('freecc:cntr:bonfires'), - cheerio = require('cheerio'); + debug = require('debug')('freecc:cntr:resources'), + cheerio = require('cheerio'), + request = require('request'); /** * GET / @@ -289,27 +290,18 @@ module.exports = { whichEnvironment: function() { return process.env.NODE_ENV; }, - getMetaData: function(req, res, next) { + getURLTitle: function(req, res, next) { var url = req.body.data.url; - var result = {}; - - client.get(url, function(siteInfo) { - var $ = cheerio.load(siteInfo); - - var meta = $('meta'); - $(meta, this).each(function () { - var prop = $(this).attr("property"), key, value; - if (prop && prop.substring(0, ns.length) === ns) { - key = prop.substring(ns.length); - value = $(this).attr("content"); - console.log("Found OGP data %s=%s", key, value); - result[key] = result[key] || []; - result[key].push(value); - } - }); - - res.json(JSON.stringify(result)); + var result = {title: ''}; + request(url, function (error, response, body) { + if (!error && response.statusCode === 200) { + var $ = cheerio.load(body); + var title = $('title').text(); + result.title = title; + debug(result); + res.json(result); + } }); } }; diff --git a/package.json b/package.json index 5ef6868d00..a443ed87cd 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "passport-oauth": "^1.0.0", "passport-twitter": "^1.0.2", "ramda": "^0.10.0", - "request": "^2.49.0", + "request": "^2.53.0", "sitemap": "^0.7.4", "uglify-js": "^2.4.15", "validator": "^3.22.1", From 9bea52d42f5480cd60ac57e1a328dfd416a30c4d Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 7 Mar 2015 17:42:22 +0900 Subject: [PATCH 31/49] Refactor post flow, get url info back from server, redirect to final submission form --- app.js | 15 +++++------ controllers/resources.js | 34 ++++++++++++++---------- controllers/story.js | 30 ++++++++++++++++++++++ views/stories/index.jade | 8 ++++-- views/stories/preliminary-submit.jade | 37 +++++++++++++++++++++++++++ views/stories/submit-story.jade | 10 ++++++-- 6 files changed, 108 insertions(+), 26 deletions(-) create mode 100644 views/stories/preliminary-submit.jade diff --git a/app.js b/app.js index 3c253644a0..fc625717da 100644 --- a/app.js +++ b/app.js @@ -317,11 +317,15 @@ app.get( storyController.submitNew ); -app.post( - '/stories/', - storyController.storySubmission +app.get( + '/stories/submit/:newStory', + storyController.preSubmit ); +app.post( + '/stories/preliminary', + storyController.newStory +); app.get( '/stories/hot', @@ -354,11 +358,6 @@ app.post( storyController.upvote ); -app.post( - '/stories/getURLMetaData', - resourcesController.getMetaData -); - /** * Challenge related routes */ diff --git a/controllers/resources.js b/controllers/resources.js index b2f271e627..104b4bd90d 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -264,7 +264,7 @@ module.exports = { return { _id: elem._id, difficulty: elem.difficulty - } + }; }) .sort(function(a, b) { return a.difficulty - b.difficulty; @@ -278,7 +278,7 @@ module.exports = { return { name: elem.name, difficulty: elem.difficulty - } + }; }) .sort(function(a, b) { return a.difficulty - b.difficulty; @@ -290,19 +290,25 @@ module.exports = { whichEnvironment: function() { return process.env.NODE_ENV; }, - getURLTitle: function(req, res, next) { - var url = req.body.data.url; + getURLTitle: function(url, callback) { + debug('getURL called initialled'); - var result = {title: ''}; - request(url, function (error, response, body) { - if (!error && response.statusCode === 200) { - var $ = cheerio.load(body); - var title = $('title').text(); - result.title = title; - debug(result); - res.json(result); - } - }); + (function () { + var result = {title: ''}; + request(url, function (error, response, body) { + debug('request fired'); + if (!error && response.statusCode === 200) { + debug('fetching data'); + var $ = cheerio.load(body); + var title = $('title').text(); + result.title = title; + debug('calling callback with', result); + callback(null, result); + } else { + callback('failed'); + } + }); + })(); } }; diff --git a/controllers/story.js b/controllers/story.js index 3f2ea24bcc..03d033885e 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -53,6 +53,16 @@ exports.recent = function(req, res, next) { }); }; +exports.preSubmit = function(req, res, next) { + var data = req.params.newStory; + debug('got presubmission with info', data.url, data.title); + res.render('stories/index', { + page: 'storySubmission', + storyURL: data.url, + storyTitle: data.title + }); +}; + exports.returnIndividualStory = function(req, res, next) { var dashedName = req.params.storyName; @@ -166,6 +176,26 @@ exports.comments = function(req, res, next) { }); }; +exports.newStory = function(req, res, next) { + var url = req.body.data.url; + debug('Got new story submission, calling resources with', url); + resources.getURLTitle(url, processResponse); + function processResponse(err, storyTitle) { + if (err) { + res.json({ + storyURL: url, + storyTitle: '' + }); + } else { + storyTitle = storyTitle ? storyTitle : ''; + res.json({ + storyURL: url, + storyTitle: storyTitle.title + }); + } + } +}; + exports.storySubmission = function(req, res, next) { var data = req.body.data; var storyLink = data.headline diff --git a/views/stories/index.jade b/views/stories/index.jade index 9e71245a01..ae2eb90ba2 100644 --- a/views/stories/index.jade +++ b/views/stories/index.jade @@ -15,6 +15,10 @@ block content if (page === 'recent') include ./new-stories if (page === 'submit') - include ./submit-story + include ./preliminary-submit if (page === 'search') - include ./search-stories \ No newline at end of file + include ./search-stories + if (page === 'storySubmission') + include ./submit-story + if (page === 'storyShow') + include ./show \ No newline at end of file diff --git a/views/stories/preliminary-submit.jade b/views/stories/preliminary-submit.jade new file mode 100644 index 0000000000..704501a92e --- /dev/null +++ b/views/stories/preliminary-submit.jade @@ -0,0 +1,37 @@ +.spacer +.col-xs-12 + form.form-horizontal.control-label-story-submission#story-submission-form + .col-xs-2.img-story-post + img.img-responsive(src='#{user.profile.picture}') + .col-xs-10 + .form-group + .col-xs-1 + label.control-label.control-label-story-submission(for='name') Link + .col-xs-11 + input#story-url.form-control(placeholder='Paste your link here', name='Link') + .spacer + .form-group + .btn.btn-big.btn-block.btn-primary#preliminary-story-submit Submit + script. + var preliminaryStorySubmit = function preliminaryStorySubmit() { + + var storyURL = $('#story-url').val(); + console.log(storyURL); + $('#preliminary-story-submit').attr('disabled', 'disabled'); + + $.post('/stories/preliminary', + { + data: { + url: storyURL + } + }) + .fail(function (xhr, textStatus, errorThrown) { + $('#preliminary-story-submit').attr('disabled', false); + }) + .done(function (data, textStatus, xhr) { + window.location = '/stories/submit/url=' + + encodeURIComponent(data.storyURL) + + '&title=' + encodeURIComponent(data.storyTitle); + }); + } + $('#preliminary-story-submit').on('click', preliminaryStorySubmit); \ No newline at end of file diff --git a/views/stories/submit-story.jade b/views/stories/submit-story.jade index a1bba32ae6..6e0dd1e6f2 100644 --- a/views/stories/submit-story.jade +++ b/views/stories/submit-story.jade @@ -1,5 +1,8 @@ .spacer .col-xs-12 + script. + var storyURL = !{JSON.stringify(storyURL)}; + var storyTitle = !{JSON.stringify(storyTitle)}; form.form-horizontal.control-label-story-submission#story-submission-form .col-xs-2.img-story-post img.img-responsive(src='#{user.profile.picture}') @@ -8,12 +11,15 @@ .col-xs-1 label.control-label.control-label-story-submission(for='name') Link .col-xs-11 - input.form-control(placeholder='Paste your link here', name='Link') + input#story-url.form-control(placeholder='Paste your link here', name='Link') .form-group .col-xs-1 label.control-label.control-label-story-submission(for='name') Title .col-xs-11 - input.form-control(placeholder='Type a headline for your link here', name='Title') + input#story-title.form-control(placeholder='Type a headline for your link here', name='Title') .spacer .form-group .btn.btn-big.btn-block.btn-primary#story-submit Submit + script. + $('#story-url').val(storyURL).attr('disabled', 'disabled'); + $('#story-title').val(storyTitle); \ No newline at end of file From a3769f65075751e48fdb7898906263e9f626c4a8 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 7 Mar 2015 18:26:49 +0900 Subject: [PATCH 32/49] Story submission now with a description, refactor show to display description above comment box --- app.js | 5 ++++ controllers/story.js | 16 +++++++++---- public/js/main.js | 12 ++++++---- views/stories/index.jade | 2 +- views/stories/news-nav.jade | 3 ++- views/stories/show.jade | 41 +++++++++++++++++---------------- views/stories/submit-story.jade | 6 +++++ 7 files changed, 53 insertions(+), 32 deletions(-) diff --git a/app.js b/app.js index fc625717da..5fc7bb9568 100644 --- a/app.js +++ b/app.js @@ -327,6 +327,11 @@ app.post( storyController.newStory ); +app.post( + '/stories/', + storyController.storySubmission +); + app.get( '/stories/hot', storyController.hot diff --git a/controllers/story.js b/controllers/story.js index 03d033885e..7ebb238905 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -54,12 +54,17 @@ exports.recent = function(req, res, next) { }; exports.preSubmit = function(req, res, next) { + debug('req params is', req.params); var data = req.params.newStory; - debug('got presubmission with info', data.url, data.title); + + data = data.replace(/url=/gi, '').replace(/&title=/gi, ',').split(','); + var url = data[0]; + var title = data[1]; + debug('result of really ugly string manipulation', url, title); res.render('stories/index', { page: 'storySubmission', - storyURL: data.url, - storyTitle: data.title + storyURL: url, + storyTitle: title }); }; @@ -92,7 +97,7 @@ exports.returnIndividualStory = function(req, res, next) { } debug('Story', story); - res.render('stories/show', { + res.render('stories/index', { title: story.headline, link: story.link, author: story.author, @@ -103,7 +108,8 @@ exports.returnIndividualStory = function(req, res, next) { id: story._id, user: req.user, timeAgo: moment(story.timePosted).fromNow(), - image: story.image + image: story.image, + page: 'show' }); }); }; diff --git a/public/js/main.js b/public/js/main.js index fa3d6e68f3..b9ffcf9b46 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -150,9 +150,11 @@ $(document).ready(function() { $('#upvote').on('click', upvoteHandler); var storySubmitButtonHandler = function storySubmitButtonHandler() { - var data = $('#story-submission-form :input'); - var link = $(data[0]).val(); - var headline = $(data[1]).val(); + + var link = $('#story-url').val(); + var headline = $('#story-title').val(); + var description = $('#description-box').val(); + console.log(link, headline, description); var userDataForUpvote = { upVotedBy: user._id, upVotedByUsername: user.profile.username @@ -164,7 +166,7 @@ $(document).ready(function() { link: link, headline: headline, timePosted: Date.now(), - description: 'TODO', + description: description, rank: 1, upVotes: [userDataForUpvote], @@ -174,7 +176,7 @@ $(document).ready(function() { username: user.profile.username }, comments: [], - image: 'http://rossmounce.co.uk/wp-content/uploads/2014/11/grumpy-cat-no-1.jpg' + image: '' } }) .fail(function (xhr, textStatus, errorThrown) { diff --git a/views/stories/index.jade b/views/stories/index.jade index ae2eb90ba2..87e487b2a5 100644 --- a/views/stories/index.jade +++ b/views/stories/index.jade @@ -20,5 +20,5 @@ block content include ./search-stories if (page === 'storySubmission') include ./submit-story - if (page === 'storyShow') + if (page === 'show') include ./show \ No newline at end of file diff --git a/views/stories/news-nav.jade b/views/stories/news-nav.jade index dd8e3e90af..a233b6f9ca 100644 --- a/views/stories/news-nav.jade +++ b/views/stories/news-nav.jade @@ -6,12 +6,13 @@ label.btn.btn-primary.btn-big(class="#{ page === 'recent' ? 'active' : '' }") input#option2(href='/stories/recent', type='radio', name='options') | New - label.btn.btn-primary.btn-big(class="#{ page === 'submit' ? 'active' : '' }") + label.btn.btn-primary.btn-big(class="#{ page === 'submit' ? 'active' : page === 'storySubmission' ? 'active' : '' }") input#option3(href='/stories/submit', type='radio', name='options') | Submit label.btn.btn-primary.btn-big(class="#{ page === 'search' ? 'active' : '' }") input#option4(href='/stories/search', type='radio', name='options') | Search + script. $('label').on('click', function() { window.location = ($(this).children('input').attr('href')); diff --git a/views/stories/show.jade b/views/stories/show.jade index c02ee316d5..a8d483c599 100644 --- a/views/stories/show.jade +++ b/views/stories/show.jade @@ -6,35 +6,36 @@ block content var comments = !{JSON.stringify(comments)}; var upVotes = !{JSON.stringify(upVotes)}; var user = !{JSON.stringify(user)}; + .spacer - .panel.panel-info - .panel-heading.text-center Camper News - .panel-body - h3.row.text-left.negative-10 - .col-xs-3.col-sm-1.text-center - .row.negative-5 - .col-xs-12.big-ion-up-arrow - a#upvote - i.ion-arrow-up-b - h3.story-up-votes - span#storyRank= rank - .col-xs-2.col-sm-1 - img(src="#{author.picture}", class='img-responsive') - .col-xs-7.col-sm-10 - .col-xs-12.negative-28 - a(href="#{link}") - h3= title + .row.col-xs-12 + h3.row.text-left.negative-10 + .col-xs-3.col-sm-1.text-center + .row.negative-5 + .col-xs-12.big-ion-up-arrow + a#upvote + i.ion-arrow-up-b + h3.story-up-votes + span#storyRank= rank + .col-xs-2.col-sm-1 + img(src="#{author.picture}", class='img-responsive') + .col-xs-7.col-sm-10 + .col-xs-12.negative-28 + a(href="#{link}") + h3= title + h6 .col-xs-12.negative-15 span Posted #{timeAgo} span  by  a(href="/" + author.username) @#{author.username} + .col-xs-10.col-xs-offset-1 + p= description .col-xs-12 - h3.row - .col-xs-12 - textarea#comment-box.form-control(name="comment-box", rows=5) + h3.row.col-xs-12 + textarea#comment-box.form-control(name="comment-box", rows=5) h3.row.btn-nav.text-center .col-xs-6.col-xs-offset-3 .btn.btn-block.btn-primary#comment-button Comment diff --git a/views/stories/submit-story.jade b/views/stories/submit-story.jade index 6e0dd1e6f2..bd6e239fe5 100644 --- a/views/stories/submit-story.jade +++ b/views/stories/submit-story.jade @@ -17,6 +17,12 @@ label.control-label.control-label-story-submission(for='name') Title .col-xs-11 input#story-title.form-control(placeholder='Type a headline for your link here', name='Title') + .form-group + .col-xs-1 + label.control-label.control-label-story-submission(for='name') + .col-xs-11 + textarea#description-box.form-control(name="comment-box", rows=5, placeholder="Start off the discussion with a description of your post") + .spacer .form-group .btn.btn-big.btn-block.btn-primary#story-submit Submit From b776be58e5aab8360027ffdd6cf7824b5b145389 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 7 Mar 2015 20:51:20 +0900 Subject: [PATCH 33/49] Finish full functionality for intial release of camper news --- controllers/resources.js | 3 - controllers/story.js | 16 +--- public/js/main.js | 6 +- views/stories/comments.jade | 108 +++++++++++++------------- views/stories/preliminary-submit.jade | 48 +++++++----- views/stories/show.jade | 23 ++---- views/stories/submit-story.jade | 2 +- 7 files changed, 96 insertions(+), 110 deletions(-) diff --git a/controllers/resources.js b/controllers/resources.js index 104b4bd90d..adb5a70940 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -291,14 +291,11 @@ module.exports = { return process.env.NODE_ENV; }, getURLTitle: function(url, callback) { - debug('getURL called initialled'); (function () { var result = {title: ''}; request(url, function (error, response, body) { - debug('request fired'); if (!error && response.statusCode === 200) { - debug('fetching data'); var $ = cheerio.load(body); var title = $('title').text(); result.title = title; diff --git a/controllers/story.js b/controllers/story.js index 7ebb238905..a69c9c9182 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -54,13 +54,11 @@ exports.recent = function(req, res, next) { }; exports.preSubmit = function(req, res, next) { - debug('req params is', req.params); var data = req.params.newStory; data = data.replace(/url=/gi, '').replace(/&title=/gi, ',').split(','); var url = data[0]; var title = data[1]; - debug('result of really ugly string manipulation', url, title); res.render('stories/index', { page: 'storySubmission', storyURL: url, @@ -74,8 +72,6 @@ exports.returnIndividualStory = function(req, res, next) { var storyName = dashedName.replace(/\-/g, ' '); - debug('looking for %s', storyName); - Story.find({'storyLink' : new RegExp(storyName, 'i')}, function(err, story) { if (err) { next(err); @@ -95,7 +91,6 @@ exports.returnIndividualStory = function(req, res, next) { if (dashedNameFull !== dashedName) { return res.redirect('../stories/' + dashedNameFull); } - debug('Story', story); res.render('stories/index', { title: story.headline, @@ -117,7 +112,6 @@ exports.returnIndividualStory = function(req, res, next) { exports.getStories = function(req, res, next) { MongoClient.connect(secrets.db, function(err, database) { var db = database; - debug('this is data', req.body.data.searchValue); db.collection('stories').find({ "$text": { "$search": req.body.data.searchValue @@ -144,7 +138,6 @@ exports.getStories = function(req, res, next) { } }).toArray(function(err, items) { if (items.length !== 0) { - debug('items found with full text', items); return res.json(items); } return res.status(404); @@ -184,7 +177,6 @@ exports.comments = function(req, res, next) { exports.newStory = function(req, res, next) { var url = req.body.data.url; - debug('Got new story submission, calling resources with', url); resources.getURLTitle(url, processResponse); function processResponse(err, storyTitle) { if (err) { @@ -228,8 +220,6 @@ exports.storySubmission = function(req, res, next) { storyLink: storyLink }); - debug('this is a story', story); - story.save(function(err, data) { if (err) { return res.status(500); @@ -241,6 +231,7 @@ exports.storySubmission = function(req, res, next) { }; exports.commentSubmit = function(req, res, next) { + debug('comment submit fired'); var data = req.body.data; var comment = new Comment({ associatedPost: data.associatedPost, @@ -255,8 +246,8 @@ exports.commentSubmit = function(req, res, next) { }; exports.commentOnCommentSubmit = function(req, res, next) { + debug('comment on comment submit'); var idToFind = req.params.id; - debug('idToFind', idToFind); var data = req.body.data; var comment = new Comment({ associatedPost: data.associatedPost, @@ -275,8 +266,7 @@ function commentSave(comment, Context, res) { if (err) { return res.status(500); } - debug('this is data from save', data); - try { + try { Context.find({'_id': comment.associatedPost}, function (err, associatedStory) { if (err) { return res.status(500); diff --git a/public/js/main.js b/public/js/main.js index b9ffcf9b46..93cbb46cb0 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -191,9 +191,11 @@ $(document).ready(function() { $('#story-submit').on('click', storySubmitButtonHandler); var commentSubmitButtonHandler = function commentSubmitButtonHandler() { + $('comment-button').unbind('click'); var data = $('#comment-box').val(); + console.log('comment clicked'); - $('#comment-button').unbind('click'); + $('#comment-button').attr('disabled', 'disabled'); $.post('/stories/comment/', { data: { @@ -207,7 +209,7 @@ $(document).ready(function() { } }) .fail(function (xhr, textStatus, errorThrown) { - $('#comment-button').bind('click', commentSubmitButtonHandler); + $('#comment-button').attr('disabled', false); }) .done(function (data, textStatus, xhr) { window.location.reload(); diff --git a/views/stories/comments.jade b/views/stories/comments.jade index cda4f4f62d..2864ba7792 100644 --- a/views/stories/comments.jade +++ b/views/stories/comments.jade @@ -26,38 +26,39 @@ $(div) .html( "
" + - "
" + - "
" + - "
" + - "

" + - commentDetails.rank + - "

" + - "
" + - "
" + - "
" + + "
" + + "
" + + "
" + + "

" + + commentDetails.rank + + "

" + + "
" + + "
" + + "
" + "
" + - "" + - "
" + - "
" + - "
" + - "
" + - commentDetails.body + - "
" + - "
" + - "
" + - "
" + - "
" + - "commented " + moment(commentDetails.commentedOn).fromNow() + " by " + - "@" + commentDetails.author.username + "" + - "
" + - "
" + - "
" + - "
" + - "
" + - "" + - "
" + - "
" + + "" + "
" + + "
" + + "
" + + "
" + + commentDetails.body + + "
" + + "
" + + "
" + + "
" + + "
" + + "commented " + moment(commentDetails.commentedOn).fromNow() + " by " + + "@" + commentDetails.author.username + "" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "Reply" + + "
" + + "
" + + "
" + "
" ) .addClass(offSetClass + ' row ' + rowClass + ' comment_' + commentDetails._id) @@ -71,39 +72,38 @@ complete: function () { sentinel--; if (!sentinel) { - console.log('Binding click handler'); - $('.comment-a-comment').on('click', 'button', function () { - $(this).attr('disabled', 'disabled'); - console.log('Unbinding click handler'); - + $('.comment-a-comment').on('click', 'a', function () { + $(this).unbind('click'); var div = document.createElement('div'); var commentId = $(this).attr('id'); $(div).html( - "" + - "" + "
" + + "" + + "" + + "
" ) .addClass('col-xs-6 col-xs-offset-3') .appendTo($(this).closest('.comment')); var submitCommentToCommentHandler = function submitCommentToCommentHandler() { $('#submit-comment-to-comment').unbind('click'); $.post('/stories/comment/' + commentId + '/comment', - { - data: { - associatedPost: commentId, - body: $('#comment-to-comment-textarea').val(), - author: { - picture: user.profile.picture, - userId: user._id, - username: user.profile.username - } - } - }) - .fail(function (xhr, textStatus, errorThrown) { - $('#submit-comment-to-comment').bind('click', submitCommentToCommentHandler); - }) - .done(function (data, textStatus, xhr) { - window.location.reload(); - }); + { + data: { + associatedPost: commentId, + body: $('#comment-to-comment-textarea').val(), + author: { + picture: user.profile.picture, + userId: user._id, + username: user.profile.username + } + } + }) + .fail(function (xhr, textStatus, errorThrown) { + $('#submit-comment-to-comment').bind('click', submitCommentToCommentHandler); + }) + .done(function (data, textStatus, xhr) { + window.location.reload(); + }); }; diff --git a/views/stories/preliminary-submit.jade b/views/stories/preliminary-submit.jade index 704501a92e..b7234f6594 100644 --- a/views/stories/preliminary-submit.jade +++ b/views/stories/preliminary-submit.jade @@ -11,27 +11,33 @@ input#story-url.form-control(placeholder='Paste your link here', name='Link') .spacer .form-group - .btn.btn-big.btn-block.btn-primary#preliminary-story-submit Submit + button.btn.btn-big.btn-block.btn-primary#preliminary-story-submit Submit script. - var preliminaryStorySubmit = function preliminaryStorySubmit() { - - var storyURL = $('#story-url').val(); - console.log(storyURL); - $('#preliminary-story-submit').attr('disabled', 'disabled'); - - $.post('/stories/preliminary', - { - data: { - url: storyURL + $('#story-url').on('keypress', function(e) { + if (e.which === 13 || e === 13) { + console.log('enter pressed'); + $('#preliminary-story-submit').click(); } - }) - .fail(function (xhr, textStatus, errorThrown) { - $('#preliminary-story-submit').attr('disabled', false); - }) - .done(function (data, textStatus, xhr) { - window.location = '/stories/submit/url=' + - encodeURIComponent(data.storyURL) + - '&title=' + encodeURIComponent(data.storyTitle); }); - } - $('#preliminary-story-submit').on('click', preliminaryStorySubmit); \ No newline at end of file + var preliminaryStorySubmit = function preliminaryStorySubmit() { + + var storyURL = $('#story-url').val(); + console.log(storyURL); + $('#preliminary-story-submit').attr('disabled', 'disabled'); + + $.post('/stories/preliminary', + { + data: { + url: storyURL + } + }) + .fail(function (xhr, textStatus, errorThrown) { + $('#preliminary-story-submit').attr('disabled', false); + }) + .done(function (data, textStatus, xhr) { + window.location = '/stories/submit/url=' + + encodeURIComponent(data.storyURL) + + '&title=' + encodeURIComponent(data.storyTitle); + }); + } + $('#preliminary-story-submit').on('click', preliminaryStorySubmit); \ No newline at end of file diff --git a/views/stories/show.jade b/views/stories/show.jade index a8d483c599..6f77bf608a 100644 --- a/views/stories/show.jade +++ b/views/stories/show.jade @@ -1,5 +1,4 @@ -extends ../layout -block content +.spacer script. var challengeName = 'Camper News'; var storyId = !{JSON.stringify(id)}; @@ -32,21 +31,13 @@ block content .col-xs-10.col-xs-offset-1 p= description - - .col-xs-12 + form.form-horizontal.control-label-story-submission + .col-xs-12 + .form-group h3.row.col-xs-12 textarea#comment-box.form-control(name="comment-box", rows=5) - h3.row.btn-nav.text-center + h3.row.text-center .col-xs-6.col-xs-offset-3 - .btn.btn-block.btn-primary#comment-button Comment + button.btn.btn-block.btn-primary#comment-button Comment - include ./comments - -// - title: story.headline, - dashedName: story.link, - author: story.author, - body: story.body, - rank: story.rank, - upVotes: story.upVotes, - comments: story.comments \ No newline at end of file + include ./comments diff --git a/views/stories/submit-story.jade b/views/stories/submit-story.jade index bd6e239fe5..8043b50dc5 100644 --- a/views/stories/submit-story.jade +++ b/views/stories/submit-story.jade @@ -25,7 +25,7 @@ .spacer .form-group - .btn.btn-big.btn-block.btn-primary#story-submit Submit + button.btn.btn-big.btn-block.btn-primary#story-submit Submit script. $('#story-url').val(storyURL).attr('disabled', 'disabled'); $('#story-title').val(storyTitle); \ No newline at end of file From 485bbcc3a905d34619eaa1ce37e98dc008042487 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 7 Mar 2015 20:57:28 +0900 Subject: [PATCH 34/49] Bring reply link up closer to comment details --- public/css/main.less | 4 ++++ views/stories/comments.jade | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/public/css/main.less b/public/css/main.less index 49b1d829d3..6be6a44943 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -196,6 +196,10 @@ ul { margin-top: -15px; } +.negative-20 { + margin-top: -20px; +} + .landing-p { font-size: 18px !important; } diff --git a/views/stories/comments.jade b/views/stories/comments.jade index 2864ba7792..6508f14cbb 100644 --- a/views/stories/comments.jade +++ b/views/stories/comments.jade @@ -52,7 +52,7 @@ "
" + "" + "
" + - "
" + + "
" + "
" + "
" + "Reply" + From 585544e1fdc28fe6e2ac20cfe81e8f3153ffc35b Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 7 Mar 2015 21:33:04 +0900 Subject: [PATCH 35/49] Minor view improvements and removing extra dom closing tags from views --- public/css/main.less | 4 +++ views/stories/comments.jade | 42 +++++++++++---------------- views/stories/hot-stories.jade | 2 +- views/stories/new-stories.jade | 52 ++++++++++++++++++---------------- 4 files changed, 49 insertions(+), 51 deletions(-) diff --git a/public/css/main.less b/public/css/main.less index 6be6a44943..0fba6d8819 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -171,6 +171,10 @@ ul { .responsive-container { position: relative; padding-bottom: 56.25%; padding-top: 30px; height: 0; overflow: hidden; } .responsive-container iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } +.positive-5 { + margin-top: 5px; +} + .negative-45 { margin-top: -45px; margin-bottom: -45px; diff --git a/views/stories/comments.jade b/views/stories/comments.jade index 6508f14cbb..f916e041a5 100644 --- a/views/stories/comments.jade +++ b/views/stories/comments.jade @@ -26,38 +26,30 @@ $(div) .html( "
" + - "
" + + "
" + + "" + + "
" + + "
" + "
" + "
" + - "

" + - commentDetails.rank + - "

" + + commentDetails.body + "
" + "
" + - "
" + - "
" + - "" + - "
" + - "
" + - "
" + - "
" + - commentDetails.body + - "
" + - "
" + - "
" + - "
" + - "
" + - "commented " + moment(commentDetails.commentedOn).fromNow() + " by " + - "@" + commentDetails.author.username + "" + - "
" + - "
" + - "
" + - "
" + - "
" + + "
" + "
" + - "Reply" + + "
" + + "commented " + moment(commentDetails.commentedOn).fromNow() + " by " + + "@" + commentDetails.author.username + "" + + "
" + "
" + "
" + + "
" + + "
" + + "
" + + "Reply" + + "
" + + "
" + + "
" + "
" + "
" ) diff --git a/views/stories/hot-stories.jade b/views/stories/hot-stories.jade index c30cd444ab..382232655c 100644 --- a/views/stories/hot-stories.jade +++ b/views/stories/hot-stories.jade @@ -19,7 +19,7 @@ h3 $(li).html("
" + "
" + - "
" + + "
" + "" + rank + "" + "
" + "
" + diff --git a/views/stories/new-stories.jade b/views/stories/new-stories.jade index 6378303a5f..bf3f2c022e 100644 --- a/views/stories/new-stories.jade +++ b/views/stories/new-stories.jade @@ -17,31 +17,33 @@ h3 var linkedName = getLinkedName(data[i].storyLink); var rank = data[i].rank; - $(li).html("
" + - "
" + - "
" + - "" + rank + "" + - "
" + - "
" + - "
" + - "" + - "
" + - "
" + - "
" + - "" + - "
" + - "Posted " + - moment(data[i].timePosted).fromNow() + - " by @" + data[i].author.username + " " + - "
" + - "
" + - "
" + - "
" + - ""); + $(li) + .html( + "
" + + "
" + + "
" + + "" + rank + "" + + "
" + + "
" + + "
" + + "" + + "
" + + "
" + + "
" + + "" + + "
" + + "Posted " + + moment(data[i].timePosted).fromNow() + + " by @" + data[i].author.username + " " + + "" + + "
" + + "
" + ) $(li).appendTo($('#story-list')); } }); \ No newline at end of file From 49886ca09773ef10da77656d1799b88f1117f37b Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sat, 7 Mar 2015 15:57:30 -0800 Subject: [PATCH 36/49] remove node-rest-client and use request --- controllers/resources.js | 43 ++++++++++------------------------------ package.json | 1 - 2 files changed, 10 insertions(+), 34 deletions(-) diff --git a/controllers/resources.js b/controllers/resources.js index adb5a70940..cf716d4d34 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -8,8 +8,7 @@ var User = require('../models/User'), bonfires = require('../seed_data/bonfires.json'), coursewares = require('../seed_data/coursewares.json'), moment = require('moment'), - Client = require('node-rest-client').Client, - client = new Client(), + https = require('https'), debug = require('debug')('freecc:cntr:resources'), cheerio = require('cheerio'), request = require('request'); @@ -26,33 +25,6 @@ module.exports = { }); }, - stats: function stats(req, res) { - var date1 = new Date("10/15/2014"); - var date2 = new Date(); - var timeDiff = Math.abs(date2.getTime() - date1.getTime()); - var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24)); - client.get('https://trello.com/1/boards/BA3xVpz9/cards?key=' + secrets.trello.key, function(trello, response) { - var nonprofitProjects = (trello && trello.length) || 15; - User.count({'points': {'$gt': 2}}, function(err, c3) { if (err) { debug('User err: ', err); next(err); } - User.count({'points': {'$gt': 9}}, function(err, c10) { if (err) { debug('User err: ', err); next(err); } - User.count({'points': {'$gt': 29}}, function(err, c30) { if (err) { debug('User err: ', err); next(err); } - User.count({'points': {'$gt': 53}}, function(err, all) { if (err) { debug('User err: ', err); next(err); } - res.render('resources/stats', { - title: 'Free Code Camp Stats:', - daysRunning: daysRunning, - nonprofitProjects: nonprofitProjects, - c3: c3, - c10: c10, - c30: c30, - all: all - }); - }); - }); - }); - }); - }); - }, - sitemap: function sitemap(req, res, next) { var appUrl = 'http://www.freecodecamp.com'; var now = moment(new Date).format('YYYY-MM-DD'); @@ -148,22 +120,27 @@ module.exports = { }, githubCalls: function(req, res) { var githubHeaders = {headers: {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1521.3 Safari/537.36'}, port:80 }; - client.get('https://api.github.com/repos/freecodecamp/freecodecamp/pulls?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function(pulls, res3) { + request('https://api.github.com/repos/freecodecamp/freecodecamp/pulls?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function(err, status1, pulls) { pulls = pulls ? Object.keys(JSON.parse(pulls)).length : "Can't connect to github"; - client.get('https://api.github.com/repos/freecodecamp/freecodecamp/issues?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function (issues, res4) { + debug('pulls', pulls); + request('https://api.github.com/repos/freecodecamp/freecodecamp/issues?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function (err, status2, issues) { + debug('issues', issues); issues = ((pulls === parseInt(pulls)) && issues) ? Object.keys(JSON.parse(issues)).length - pulls : "Can't connect to GitHub"; res.send({"issues": issues, "pulls" : pulls}); }); }); }, + + + trelloCalls: function(req, res) { - client.get('https://trello.com/1/boards/BA3xVpz9/cards?key=' + secrets.trello.key, function(trello) { + request('https://trello.com/1/boards/BA3xVpz9/cards?key=' + secrets.trello.key, function(err, status, trello) { trello = trello ? (JSON.parse(trello)).length : "Can't connect to to Trello"; res.send({"trello": trello}); }); }, bloggerCalls: function(req, res) { - client.get('https://www.googleapis.com/blogger/v3/blogs/2421288658305323950/posts?key=' + secrets.blogger.key, function (blog) { + request('https://www.googleapis.com/blogger/v3/blogs/2421288658305323950/posts?key=' + secrets.blogger.key, function (err, status, blog) { var blog = blog.length > 100 ? JSON.parse(blog) : ""; res.send({ blog1Title: blog ? blog["items"][0]["title"] : "Can't connect to Blogger", diff --git a/package.json b/package.json index a443ed87cd..f44369004e 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ "morgan": "^1.5.0", "newrelic": "^1.13.3", "node": "0.0.0", - "node-rest-client": "^1.4.3", "nodemailer": "^1.3.0", "passport": "^0.2.1", "passport-facebook": "^1.0.3", From 87ac659038d634765b765a8a5add990ee10b18fd Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sat, 7 Mar 2015 16:48:57 -0800 Subject: [PATCH 37/49] tighten up the interface for mobile --- views/stories/index.jade | 8 ++++- views/stories/news-nav.jade | 45 +++++++++++++++++++-------- views/stories/preliminary-submit.jade | 33 ++++++++++++-------- views/stories/search-stories.jade | 2 +- views/stories/submit-story.jade | 18 +++++------ 5 files changed, 68 insertions(+), 38 deletions(-) diff --git a/views/stories/index.jade b/views/stories/index.jade index 87e487b2a5..c5b0c2de34 100644 --- a/views/stories/index.jade +++ b/views/stories/index.jade @@ -15,7 +15,13 @@ block content if (page === 'recent') include ./new-stories if (page === 'submit') - include ./preliminary-submit + if (user) + include ./preliminary-submit + else + .spacer + .text-center + a.btn.btn-cta.signup-btn.btn-primary(href="/login") Sign in to post your story (it's free) + .spacer if (page === 'search') include ./search-stories if (page === 'storySubmission') diff --git a/views/stories/news-nav.jade b/views/stories/news-nav.jade index a233b6f9ca..0980167c31 100644 --- a/views/stories/news-nav.jade +++ b/views/stories/news-nav.jade @@ -1,17 +1,36 @@ .col-xs-12 - .btn-group.input-group.btn-group-justified(data-toggle='buttons') - label.btn.btn-primary.btn-big(class="#{ page === 'hot' ? 'active' : '' }") - input#option1(href='/stories/hot', type='radio', name='options') - | Hot - label.btn.btn-primary.btn-big(class="#{ page === 'recent' ? 'active' : '' }") - input#option2(href='/stories/recent', type='radio', name='options') - | New - label.btn.btn-primary.btn-big(class="#{ page === 'submit' ? 'active' : page === 'storySubmission' ? 'active' : '' }") - input#option3(href='/stories/submit', type='radio', name='options') - | Submit - label.btn.btn-primary.btn-big(class="#{ page === 'search' ? 'active' : '' }") - input#option4(href='/stories/search', type='radio', name='options') - | Search + .visible-xs.visible-sm + .btn-group.input-group.btn-group-justified(data-toggle='buttons') + label.btn.btn-primary.btn-big(class="#{ page === 'hot' ? 'active' : '' }") + input#option1(href='/stories/hot', type='radio', name='options') + i.ion-ios-pulse-strong + label.btn.btn-primary.btn-big(class="#{ page === 'recent' ? 'active' : '' }") + input#option2(href='/stories/recent', type='radio', name='options') + i.ion-ios-clock + label.btn.btn-primary.btn-big(class="#{ page === 'submit' ? 'active' : page === 'storySubmission' ? 'active' : '' }") + input#option3(href='/stories/submit', type='radio', name='options') + i.ion-plus-circled + label.btn.btn-primary.btn-big(class="#{ page === 'search' ? 'active' : '' }") + input#option4(href='/stories/search', type='radio', name='options') + i.ion-search + .visible-md.visible-lg + .btn-group.input-group.btn-group-justified(data-toggle='buttons') + label.btn.btn-primary.btn-big(class="#{ page === 'hot' ? 'active' : '' }") + input#option1(href='/stories/hot', type='radio', name='options') + i.ion-ios-pulse-strong   + | Hot + label.btn.btn-primary.btn-big(class="#{ page === 'recent' ? 'active' : '' }") + input#option2(href='/stories/recent', type='radio', name='options') + i.ion-ios-clock   + | New + label.btn.btn-primary.btn-big(class="#{ page === 'submit' ? 'active' : page === 'storySubmission' ? 'active' : '' }") + input#option3(href='/stories/submit', type='radio', name='options') + i.ion-plus-circled   + | Submit + label.btn.btn-primary.btn-big(class="#{ page === 'search' ? 'active' : '' }") + input#option4(href='/stories/search', type='radio', name='options') + i.ion-search   + | Search script. $('label').on('click', function() { diff --git a/views/stories/preliminary-submit.jade b/views/stories/preliminary-submit.jade index b7234f6594..3937b6a800 100644 --- a/views/stories/preliminary-submit.jade +++ b/views/stories/preliminary-submit.jade @@ -1,17 +1,24 @@ -.spacer .col-xs-12 - form.form-horizontal.control-label-story-submission#story-submission-form - .col-xs-2.img-story-post - img.img-responsive(src='#{user.profile.picture}') - .col-xs-10 - .form-group - .col-xs-1 - label.control-label.control-label-story-submission(for='name') Link - .col-xs-11 - input#story-url.form-control(placeholder='Paste your link here', name='Link') - .spacer - .form-group - button.btn.btn-big.btn-block.btn-primary#preliminary-story-submit Submit + .spacer + .input-group + input#story-url.big-text-field.form-control(placeholder='Paste your link here', name='Link', autofocus) + span.input-group-btn + button#preliminary-story-submit.btn.btn-big.btn-primary(type='button') Submit + .spacer + + + // + .col-xs-12 + form.form-horizontal.control-label-story-submission#story-submission-form + .col-xs-2.img-story-post + img.img-responsive(src='#{user.profile.picture}') + .col-xs-10 + .form-group + .col-xs-11 + input#story-url.form-control(placeholder='Paste your link here', name='Link') + .spacer + .form-group + button.btn.btn-big.btn-block.btn-primary Submit script. $('#story-url').on('keypress', function(e) { if (e.which === 13 || e === 13) { diff --git a/views/stories/search-stories.jade b/views/stories/search-stories.jade index 19926f8be0..f1140493c7 100644 --- a/views/stories/search-stories.jade +++ b/views/stories/search-stories.jade @@ -1,7 +1,7 @@ .col-xs-12 .spacer .input-group - input#searchArea.big-text-field.form-control(type='text', placeholder='Search for...', autofocus) + input#searchArea.big-text-field.form-control(type='text', placeholder='Search our stories', autofocus) span.input-group-btn button#searchbutton.btn.btn-big.btn-primary(type='button') Search .spacer diff --git a/views/stories/submit-story.jade b/views/stories/submit-story.jade index 8043b50dc5..df601fcbfd 100644 --- a/views/stories/submit-story.jade +++ b/views/stories/submit-story.jade @@ -4,23 +4,21 @@ var storyURL = !{JSON.stringify(storyURL)}; var storyTitle = !{JSON.stringify(storyTitle)}; form.form-horizontal.control-label-story-submission#story-submission-form - .col-xs-2.img-story-post - img.img-responsive(src='#{user.profile.picture}') - .col-xs-10 + .col-xs-12 .form-group - .col-xs-1 + .col-xs-12.col-md-1 label.control-label.control-label-story-submission(for='name') Link - .col-xs-11 + .col-xs-12.col-md-11 input#story-url.form-control(placeholder='Paste your link here', name='Link') .form-group - .col-xs-1 + .col-xs-12.col-md-1 label.control-label.control-label-story-submission(for='name') Title - .col-xs-11 + .col-xs-12.col-md-11 input#story-title.form-control(placeholder='Type a headline for your link here', name='Title') .form-group - .col-xs-1 - label.control-label.control-label-story-submission(for='name') - .col-xs-11 + .col-xs-12.col-md-1 + label.control-label.control-label-story-submission(for='name') Description + .col-xs-12.col-md-11 textarea#description-box.form-control(name="comment-box", rows=5, placeholder="Start off the discussion with a description of your post") .spacer From 0a741f0e542c1332cfe01a5768eede3c9bf2ec7d Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sat, 7 Mar 2015 17:38:47 -0800 Subject: [PATCH 38/49] further ux improvements to branch --- controllers/story.js | 24 ++++++++++++++++++++++++ views/stories/preliminary-submit.jade | 3 +++ views/stories/show.jade | 3 +-- views/stories/submit-story.jade | 2 +- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/controllers/story.js b/controllers/story.js index a69c9c9182..e44c4d7dad 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -54,8 +54,10 @@ exports.recent = function(req, res, next) { }; exports.preSubmit = function(req, res, next) { + var data = req.params.newStory; + data = data.replace(/url=/gi, '').replace(/&title=/gi, ',').split(','); var url = data[0]; var title = data[1]; @@ -177,16 +179,38 @@ exports.comments = function(req, res, next) { exports.newStory = function(req, res, next) { var url = req.body.data.url; + debug('In pre submit with a url', url); + + Story.find({'link': url}, function(err, story) { + debug('Attempting to find a story'); + if (err) { + debug('oops'); + return res.status(500); + } + if (story.length) { + debug('Found a story already, here\'s the return from find', story); + req.flash('errors', { + msg: "Someone's already posted that link. Here's the discussion." + }); + debug('Redirecting the user with', story[0].storyLink); + res.json({ + alreadyPosted: true, + storyURL: story.pop().storyLink + }); + } + }); resources.getURLTitle(url, processResponse); function processResponse(err, storyTitle) { if (err) { res.json({ + alreadyPosted: false, storyURL: url, storyTitle: '' }); } else { storyTitle = storyTitle ? storyTitle : ''; res.json({ + alreadyPosted: false, storyURL: url, storyTitle: storyTitle.title }); diff --git a/views/stories/preliminary-submit.jade b/views/stories/preliminary-submit.jade index 3937b6a800..f8485ff26c 100644 --- a/views/stories/preliminary-submit.jade +++ b/views/stories/preliminary-submit.jade @@ -42,6 +42,9 @@ $('#preliminary-story-submit').attr('disabled', false); }) .done(function (data, textStatus, xhr) { + if (data.alreadyPosted) { + window.location = '/stories/' + data.storyURL; + } window.location = '/stories/submit/url=' + encodeURIComponent(data.storyURL) + '&title=' + encodeURIComponent(data.storyTitle); diff --git a/views/stories/show.jade b/views/stories/show.jade index 6f77bf608a..59764476c1 100644 --- a/views/stories/show.jade +++ b/views/stories/show.jade @@ -26,8 +26,7 @@ .col-xs-12.negative-15 span Posted #{timeAgo} span  by  - a(href="/" + author.username) - @#{author.username} + a(href="/" + author.username) @#{author.username} .col-xs-10.col-xs-offset-1 p= description diff --git a/views/stories/submit-story.jade b/views/stories/submit-story.jade index df601fcbfd..cd67e38cd3 100644 --- a/views/stories/submit-story.jade +++ b/views/stories/submit-story.jade @@ -14,7 +14,7 @@ .col-xs-12.col-md-1 label.control-label.control-label-story-submission(for='name') Title .col-xs-12.col-md-11 - input#story-title.form-control(placeholder='Type a headline for your link here', name='Title') + input#story-title.form-control(placeholder='Type a headline for your link here', name='Title', autofocus) .form-group .col-xs-12.col-md-1 label.control-label.control-label-story-submission(for='name') Description From 790a2d987bbf241e298edfe54b7134cdd4b6f321 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sun, 8 Mar 2015 11:53:03 +0900 Subject: [PATCH 39/49] Fighting CSS --- controllers/story.js | 8 +++++-- public/css/main.less | 12 ++++++++++ public/js/main.js | 6 +++++ views/stories/preliminary-submit.jade | 34 +++++++++++---------------- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/controllers/story.js b/controllers/story.js index e44c4d7dad..11c6fa9b19 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -179,6 +179,9 @@ exports.comments = function(req, res, next) { exports.newStory = function(req, res, next) { var url = req.body.data.url; + if (url.search(/^https?:\/\//g) === -1) { + url = 'http://' + url; + } debug('In pre submit with a url', url); Story.find({'link': url}, function(err, story) { @@ -193,13 +196,14 @@ exports.newStory = function(req, res, next) { msg: "Someone's already posted that link. Here's the discussion." }); debug('Redirecting the user with', story[0].storyLink); - res.json({ + return res.json({ alreadyPosted: true, storyURL: story.pop().storyLink }); } + resources.getURLTitle(url, processResponse); }); - resources.getURLTitle(url, processResponse); + function processResponse(err, storyTitle) { if (err) { res.json({ diff --git a/public/css/main.less b/public/css/main.less index 0fba6d8819..3a95c021aa 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -395,9 +395,21 @@ ul { font-size: 30px; } +.btn-small { + font-size: 30px; + @media (max-width: 768px) { + font-size: 15px; + height: 26.5px; + padding-top: 1px; + } +} .big-text-field { font-size: 30px; height: 57px; + @media (max-width: 768px) { + font-size: 15px; + height: 26.5px; + } } .table { diff --git a/public/js/main.js b/public/js/main.js index 93cbb46cb0..22c2f0751c 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -253,6 +253,12 @@ profileValidation.controller('emailSignInController', ['$scope', } ]); +profileValidation.controller('URLSubmitController', ['$scope', + function($scope) { + + } +]); + profileValidation.controller('nonprofitFormController', ['$scope', function($scope) { diff --git a/views/stories/preliminary-submit.jade b/views/stories/preliminary-submit.jade index f8485ff26c..70ca4bf601 100644 --- a/views/stories/preliminary-submit.jade +++ b/views/stories/preliminary-submit.jade @@ -1,24 +1,17 @@ .col-xs-12 .spacer - .input-group - input#story-url.big-text-field.form-control(placeholder='Paste your link here', name='Link', autofocus) - span.input-group-btn - button#preliminary-story-submit.btn.btn-big.btn-primary(type='button') Submit + div(ng-controller="URLSubmitController") + + form.input-group(name='URLSubmit') + input#story-url.big-text-field.form-control(placeholder='Paste your link here', name='link', type='url', ng-model='possibleURL' autofocus) + span.input-group-btn + button#preliminary-story-submit.btn.btn-big.btn-primary(type='button', ng-disabled='URLSubmit.link.$invalid') Submit + div(ng-show="URLSubmit.link.$error.url && !URLsubmit.link.$pristine") + alert(type='danger') + span.ion-close-circled + | Please enter a valid URL format (http://www.example.com/). .spacer - - // - .col-xs-12 - form.form-horizontal.control-label-story-submission#story-submission-form - .col-xs-2.img-story-post - img.img-responsive(src='#{user.profile.picture}') - .col-xs-10 - .form-group - .col-xs-11 - input#story-url.form-control(placeholder='Paste your link here', name='Link') - .spacer - .form-group - button.btn.btn-big.btn-block.btn-primary Submit script. $('#story-url').on('keypress', function(e) { if (e.which === 13 || e === 13) { @@ -44,10 +37,11 @@ .done(function (data, textStatus, xhr) { if (data.alreadyPosted) { window.location = '/stories/' + data.storyURL; + } else { + window.location = '/stories/submit/url=' + + encodeURIComponent(data.storyURL) + + '&title=' + encodeURIComponent(data.storyTitle); } - window.location = '/stories/submit/url=' + - encodeURIComponent(data.storyURL) + - '&title=' + encodeURIComponent(data.storyTitle); }); } $('#preliminary-story-submit').on('click', preliminaryStorySubmit); \ No newline at end of file From c8a9f6f81083fae05435ea21981913f34721f65e Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sat, 7 Mar 2015 19:03:26 -0800 Subject: [PATCH 40/49] add benign class that makes search and submit fields on news right size on mobile --- public/css/main.less | 11 +++++++---- views/stories/preliminary-submit.jade | 4 ++-- views/stories/search-stories.jade | 4 ++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/public/css/main.less b/public/css/main.less index 3a95c021aa..bec9f26513 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -395,17 +395,20 @@ ul { font-size: 30px; } -.btn-small { +.big-text-field { font-size: 30px; + height: 57px; +} + +.btn-responsive { @media (max-width: 768px) { font-size: 15px; height: 26.5px; padding-top: 1px; } } -.big-text-field { - font-size: 30px; - height: 57px; + +.field-responsive { @media (max-width: 768px) { font-size: 15px; height: 26.5px; diff --git a/views/stories/preliminary-submit.jade b/views/stories/preliminary-submit.jade index 70ca4bf601..293445f2c5 100644 --- a/views/stories/preliminary-submit.jade +++ b/views/stories/preliminary-submit.jade @@ -3,9 +3,9 @@ div(ng-controller="URLSubmitController") form.input-group(name='URLSubmit') - input#story-url.big-text-field.form-control(placeholder='Paste your link here', name='link', type='url', ng-model='possibleURL' autofocus) + input#story-url.big-text-field.field-responsive.form-control(placeholder='Paste your link here', name='link', type='url', ng-model='possibleURL' autofocus) span.input-group-btn - button#preliminary-story-submit.btn.btn-big.btn-primary(type='button', ng-disabled='URLSubmit.link.$invalid') Submit + button#preliminary-story-submit.btn.btn-big.btn-primary.btn-responsive(type='button', ng-disabled='URLSubmit.link.$invalid') Submit div(ng-show="URLSubmit.link.$error.url && !URLsubmit.link.$pristine") alert(type='danger') span.ion-close-circled diff --git a/views/stories/search-stories.jade b/views/stories/search-stories.jade index f1140493c7..8a56a0664d 100644 --- a/views/stories/search-stories.jade +++ b/views/stories/search-stories.jade @@ -1,9 +1,9 @@ .col-xs-12 .spacer .input-group - input#searchArea.big-text-field.form-control(type='text', placeholder='Search our stories', autofocus) + input#searchArea.big-text-field.field-responsive.form-control(type='text', placeholder='Search our stories', autofocus) span.input-group-btn - button#searchbutton.btn.btn-big.btn-primary(type='button') Search + button#searchbutton.btn.btn-big.btn-primary.btn-responsive(type='button') Search .spacer #story-list From 1edc1aa3cf5f6d0f89e9d227c208ece9aa772f5d Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sun, 8 Mar 2015 12:32:35 +0900 Subject: [PATCH 41/49] Begin refactoring story lists to order list, remove separate rank div --- controllers/story.js | 2 +- public/css/main.less | 5 ++++ views/stories/hot-stories.jade | 47 +++++++++++++++++----------------- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/controllers/story.js b/controllers/story.js index 11c6fa9b19..5bbca93aad 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -10,7 +10,7 @@ var R = require('ramda'), secrets = require('../config/secrets'); exports.hotJSON = function(req, res, next) { - var story = Story.find({}).sort({'rank': -1}); + var story = Story.find({}).sort({'rank': -1, 'timePosted': -1}); story.exec(function(err, stories) { if (err) { throw err; diff --git a/public/css/main.less b/public/css/main.less index bec9f26513..1207426824 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -805,6 +805,11 @@ iframe.iphone { padding: 15px 0 15px 0; } +.img-news { + width: 50px; + height: 50px; +} + //uncomment this to see the dimensions of all elements outlined in red //* { // border-color: red; diff --git a/views/stories/hot-stories.jade b/views/stories/hot-stories.jade index 382232655c..493463b9d8 100644 --- a/views/stories/hot-stories.jade +++ b/views/stories/hot-stories.jade @@ -1,5 +1,5 @@ -h3 - ul#story-list.story-list +.col-xs-12 + ol#story-list.story-list script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") script. @@ -17,31 +17,30 @@ h3 var linkedName = getLinkedName(data[i].storyLink); var rank = data[i].rank; - $(li).html("
" + - "
" + - "
" + - "" + rank + "" + - "
" + + $(li) + .html( + "
" + + "
" + + "" + + "
" + + "
" + + "
" + + "" + - "
" + - "" + - "
" + - "
" + - "
" + - "" + - "
" + - "Posted " + - moment(data[i].timePosted).fromNow() + - " by @" + data[i].author.username + " " + - "
" + - "
" + + "
" + + "" + + rank + " points, posted " + + moment(data[i].timePosted).fromNow() + + " by @" + data[i].author.username + " " + + "" + "
" + "
" + - ""); + "
" + + "
" + ); $(li).appendTo($('#story-list')); } }); \ No newline at end of file From bac22c1d96ac87b1f62ab646e358d2c18ad835d1 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sun, 8 Mar 2015 12:48:02 +0900 Subject: [PATCH 42/49] Space hot stories view, remove all listing and refactor to divs --- public/css/main.less | 5 +++-- views/stories/hot-stories.jade | 15 ++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/public/css/main.less b/public/css/main.less index 1207426824..b3e4ad7bb4 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -775,8 +775,9 @@ iframe.iphone { margin-bottom: -4px; } -.story-list li { - padding-bottom: 5px; +.story-list { + padding-bottom: 30px; + margin-bottom: 30px; } .big-ion-up-arrow { diff --git a/views/stories/hot-stories.jade b/views/stories/hot-stories.jade index 493463b9d8..501bb25d5c 100644 --- a/views/stories/hot-stories.jade +++ b/views/stories/hot-stories.jade @@ -1,8 +1,9 @@ .col-xs-12 - ol#story-list.story-list + #story-list.story-list script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") script. + var getLinkedName = function getLinkedName(name) { return name.trim().toLowerCase().replace(/\s/g, '-'); } @@ -13,17 +14,20 @@ .success( function(data) { for (var i = 0; i < data.length; i++) { - var li = document.createElement('li'); + var div = document.createElement('div'); var linkedName = getLinkedName(data[i].storyLink); var rank = data[i].rank; - $(li) + $(div) .html( "
" + + "
" + + (i + 1) + + "
" + "
" + "" + "
" + - "
" + + "
" + "" ); - $(li).appendTo($('#story-list')); + $(div).addClass('story-list') + $(div).appendTo($('#story-list')); } }); \ No newline at end of file From 7b7f808385c1da7373ddba19a5c28a810aa57af6 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sat, 7 Mar 2015 22:20:29 -0800 Subject: [PATCH 43/49] minor fix to the showHTML view. No longer has awkward horizontal scroll, and should prevent this UX nonstarter throughout app now --- public/css/main.less | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/css/main.less b/public/css/main.less index bec9f26513..3baaa5a44b 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -27,6 +27,8 @@ li, .wrappable { html { position: relative; min-height: 100%; + // hack to prevent horizontal overflow problem on showHTML view + overflow-x: hidden; } body.full-screen-body-background { From f367a0310ee89edceeca5ed8feeca5a0af9a2858 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sun, 8 Mar 2015 16:50:51 +0900 Subject: [PATCH 44/49] Finalizing display issues for news release --- controllers/resources.js | 50 +++++++++++++++++++++++++--- controllers/story.js | 3 +- controllers/user.js | 14 +++++--- models/Comment.js | 9 +++++- models/Story.js | 13 ++++++++ public/css/main.less | 12 +++++++ views/stories/comments.jade | 42 +++++++++++------------- views/stories/hot-stories.jade | 1 + views/stories/new-stories.jade | 59 ++++++++++++++++++---------------- views/stories/show.jade | 20 +++++++++--- 10 files changed, 156 insertions(+), 67 deletions(-) diff --git a/controllers/resources.js b/controllers/resources.js index cf716d4d34..b47a273158 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -1,6 +1,8 @@ var User = require('../models/User'), Challenge = require('./../models/Challenge'), Bonfire = require('./../models/Bonfire'), + Story = require('./../models/Story'), + Comment = require('./../models/Comment'), resources = require('./resources.json'), questions = resources.questions, steps = resources.steps, @@ -11,7 +13,8 @@ var User = require('../models/User'), https = require('https'), debug = require('debug')('freecc:cntr:resources'), cheerio = require('cheerio'), - request = require('request'); + request = require('request'), + R = require('ramda'); /** * GET / @@ -283,8 +286,47 @@ module.exports = { } }); })(); + }, + updateUserStoryPictures: function(userId, picture) { + + var counter = 0, + foundStories, + foundComments; + + Story.find({'author.userId': userId}, function(err, stories) { + if (err) { + throw err; + } + foundStories = stories; + counter++; + saveStoriesAndComments(); + }); + Comment.find({'author.userId': userId}, function(err, comments) { + if (err) { + throw err; + } + foundComments = comments; + counter++; + saveStoriesAndComments(); + }); + + function saveStoriesAndComments() { + if (counter !== 2) { + return; + } + R.forEach(function(comment) { + comment.author.picture = picture; + comment.markModified('author'); + comment.save(); + }, foundComments); + + R.forEach(function(story) { + story.author.picture = picture; + debug('This is a story', story); + debug(story.author.picture); + story.markModified('author'); + story.save(); + }, foundStories); + } } }; - - - diff --git a/controllers/story.js b/controllers/story.js index 5bbca93aad..b3eab98834 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -7,7 +7,8 @@ var R = require('ramda'), resources = require('./resources'), mongodb = require('mongodb'), MongoClient = mongodb.MongoClient, - secrets = require('../config/secrets'); + secrets = require('../config/secrets'), + User = require('./../models/User'); exports.hotJSON = function(req, res, next) { var story = Story.find({}).sort({'rank': -1, 'timePosted': -1}); diff --git a/controllers/user.js b/controllers/user.js index c7947d53a9..c0da54bf47 100644 --- a/controllers/user.js +++ b/controllers/user.js @@ -7,7 +7,8 @@ var _ = require('lodash'), secrets = require('../config/secrets'), moment = require('moment'), Challenge = require('./../models/Challenge'), - debug = require('debug')('freecc:cntr:challenges'); + debug = require('debug')('freecc:cntr:challenges') + resources = require('./resources'); //TODO(Berks): Refactor to use module.exports = {} pattern. @@ -314,7 +315,7 @@ exports.postUpdateProfile = function(req, res, next) { return next(err); } var user = req.user; - if (existingUsername && existingUsername.profile.username != user.profile.username) { + if (existingUsername && existingUsername.profile.username !== user.profile.username) { req.flash('errors', { msg: 'An account with that username already exists.' }); @@ -343,9 +344,12 @@ exports.postUpdateProfile = function(req, res, next) { user.save(function (err) { - if (err) return next(err); - req.flash('success', {msg: 'Profile information updated.'}); - res.redirect('/account'); + if (err) { + return next(err); + } + req.flash('success', {msg: 'Profile information updated.'}); + res.redirect('/account'); + resources.updateUserStoryPictures(user._id.toString(), user.profile.picture); }); }); }); diff --git a/models/Comment.js b/models/Comment.js index 8af4aaefcf..89d1804654 100644 --- a/models/Comment.js +++ b/models/Comment.js @@ -29,4 +29,11 @@ var commentSchema = new mongoose.Schema({ } }); -module.exports = mongoose.model('Comment', commentSchema); \ No newline at end of file +module.exports = mongoose.model('Comment', commentSchema); + +/* + author: { + type: mongoose.Schema.Types.ObjectId, + ref: 'User' + }, + */ \ No newline at end of file diff --git a/models/Story.js b/models/Story.js index 902ff2c893..76d95471a5 100644 --- a/models/Story.js +++ b/models/Story.js @@ -41,5 +41,18 @@ var storySchema = new mongoose.Schema({ } }); +storySchema.pre('save', function(next) { + console.log('pre save test'); + next(); +}); + module.exports = mongoose.model('Story', storySchema); +/* + author: { + userId: { + type: mongoose.Schema.Types.ObjectId, + ref: 'User' + } + }, + */ diff --git a/public/css/main.less b/public/css/main.less index b3e4ad7bb4..c12a897789 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -192,6 +192,10 @@ ul { margin-top: -35px; } +.negative-30 { + margin-top: -30px; +} + .negative-5 { margin-top: -5px; } @@ -811,6 +815,14 @@ iframe.iphone { height: 50px; } +.comment-wrapper { + position: relative; +} + +.comment + .comment-wrapper { + left: 3%; +} + //uncomment this to see the dimensions of all elements outlined in red //* { // border-color: red; diff --git a/views/stories/comments.jade b/views/stories/comments.jade index f916e041a5..b85cd7e7e0 100644 --- a/views/stories/comments.jade +++ b/views/stories/comments.jade @@ -25,35 +25,29 @@ var div = document.createElement('div'); $(div) .html( - "
" + - "
" + - "" + - "
" + - "
" + - "
" + - "
" + - commentDetails.body + - "
" + + "
" + + "
" + + "" + "
" + - "
" + - "
" + - "
" + + "
" + + "
" + "
" + - "Reply" + + "
" + + "commented " + moment(commentDetails.commentedOn).fromNow() + " by " + + "@" + commentDetails.author.username + " · " + + "Reply" + + "
" + "
" + "
" + "
" + - "
" + - "
" - ) - .addClass(offSetClass + ' row ' + rowClass + ' comment_' + commentDetails._id) + "
" + ) + .addClass('comment-wrapper comment_' + commentDetails._id) .appendTo($(containerSelector)); sentinel += commentDetails.comments.length; @@ -70,7 +64,7 @@ var commentId = $(this).attr('id'); $(div).html( "
" + - "" + + "" + "" + "
" ) diff --git a/views/stories/hot-stories.jade b/views/stories/hot-stories.jade index 501bb25d5c..9949b3deb6 100644 --- a/views/stories/hot-stories.jade +++ b/views/stories/hot-stories.jade @@ -1,3 +1,4 @@ +.spacer .col-xs-12 #story-list.story-list diff --git a/views/stories/new-stories.jade b/views/stories/new-stories.jade index bf3f2c022e..93b9b25e58 100644 --- a/views/stories/new-stories.jade +++ b/views/stories/new-stories.jade @@ -1,10 +1,12 @@ -h3 - ul#story-list.story-list +.spacer +.col-xs-12 + #story-list.story-list script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") script. + var getLinkedName = function getLinkedName(name) { - return name.toLowerCase().replace(/\s/g, '-'); + return name.trim().toLowerCase().replace(/\s/g, '-'); } $.ajax({ url: '/stories/recentStories', @@ -13,37 +15,38 @@ h3 .success( function(data) { for (var i = 0; i < data.length; i++) { - var li = document.createElement('li'); + var div = document.createElement('div'); var linkedName = getLinkedName(data[i].storyLink); var rank = data[i].rank; - $(li) + $(div) .html( - "
" + - "
" + - "
" + - "" + rank + "" + + "
" + + "
" + + (i + 1) + "
" + - "
" + - "
" + - "" + - "
" + - "
" + - "
" + - "
" + - "" - + data[i].storyLink + - "" + + "
" + + "" + + "
" + + "
" + + "
" + + "" + + "
" + + "" + + rank + " points, posted " + + moment(data[i].timePosted).fromNow() + + " by @" + data[i].author.username + " " + + "" + + "
" + "
" + - "
" + - "Posted " + - moment(data[i].timePosted).fromNow() + - " by @" + data[i].author.username + " " + - "" + - "
" + - "
" - ) - $(li).appendTo($('#story-list')); + "
" + ); + $(div).addClass('story-list') + $(div).appendTo($('#story-list')); } }); \ No newline at end of file diff --git a/views/stories/show.jade b/views/stories/show.jade index 59764476c1..0d1d20cf08 100644 --- a/views/stories/show.jade +++ b/views/stories/show.jade @@ -17,7 +17,7 @@ h3.story-up-votes span#storyRank= rank .col-xs-2.col-sm-1 - img(src="#{author.picture}", class='img-responsive') + img(src="#{author.picture}", class='img-news') .col-xs-7.col-sm-10 .col-xs-12.negative-28 a(href="#{link}") @@ -26,17 +26,29 @@ .col-xs-12.negative-15 span Posted #{timeAgo} span  by  - a(href="/" + author.username) @#{author.username} + a(href="/" + author.username) + | @#{author.username} + |  Â·  + a#reply-to-main-post Reply .col-xs-10.col-xs-offset-1 p= description + .col-xs-12#reply-area - form.form-horizontal.control-label-story-submission + + + .hidden-element#initial-comment-submit + form.form-horizontal.control-label-story-submission .col-xs-12 .form-group - h3.row.col-xs-12 + h3.row textarea#comment-box.form-control(name="comment-box", rows=5) h3.row.text-center .col-xs-6.col-xs-offset-3 button.btn.btn-block.btn-primary#comment-button Comment + script. + $('#reply-to-main-post').on('click', function() { + $('#initial-comment-submit').removeClass('hidden-element'); + $(this).unbind('click'); + }); include ./comments From aacb0070b3738f586df729a75f0dc266ef892a93 Mon Sep 17 00:00:00 2001 From: Michael Q Larson Date: Sun, 8 Mar 2015 01:36:09 -0800 Subject: [PATCH 45/49] add the nonprofit projects guide view and change all our nonbreaking spaces to smaller, more natural looking spaces --- app.js | 1 + controllers/resources.js | 6 ++ views/account/account.jade | 4 +- views/account/email-signin.jade | 2 +- views/bonfire/show.jade | 4 +- views/challenges/show.jade | 2 +- views/contact/nonprofits.jade | 2 +- views/coursewares/showJS.jade | 2 +- views/coursewares/showVideo.jade | 2 +- views/home.jade | 18 ++-- views/partials/bonfires.jade | 2 +- views/partials/challenges.jade | 2 +- views/partials/footer.jade | 12 +-- views/partials/navbar.jade | 6 +- views/resources/chat.jade | 12 +-- views/resources/deploy-a-website.jade | 8 +- .../guide-to-our-nonprofit-projects.jade | 96 +++++++++++++++++++ views/resources/install-screenhero.jade | 4 +- views/resources/live-pair-programming.jade | 12 +-- .../nonprofit-project-instructions.jade | 10 +- views/resources/privacy.jade | 2 +- views/stories/news-nav.jade | 8 +- views/stories/show.jade | 2 +- 23 files changed, 161 insertions(+), 58 deletions(-) create mode 100644 views/resources/guide-to-our-nonprofit-projects.jade diff --git a/app.js b/app.js index 5fc7bb9568..c88fd0b5e3 100644 --- a/app.js +++ b/app.js @@ -224,6 +224,7 @@ app.get('/chat', resourcesController.chat); app.get('/live-pair-programming', resourcesController.livePairProgramming); app.get('/install-screenhero', resourcesController.installScreenHero); app.get('/javascript-in-your-inbox', resourcesController.javaScriptInYourInbox); +app.get('/guide-to-our-nonprofit-projects', resourcesController.guideToOurNonprofitProjects); app.get('/chromebook', resourcesController.chromebook); app.get('/deploy-a-website', resourcesController.deployAWebsite); app.get('/gmail-shortcuts', resourcesController.gmailShortcuts); diff --git a/controllers/resources.js b/controllers/resources.js index cf716d4d34..46f6a4341b 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -83,6 +83,12 @@ module.exports = { }); }, + guideToOurNonprofitProjects: function guideToOurNonprofitProjects(req, res) { + res.render('resources/guide-to-our-nonprofit-projects', { + title: 'A guide to our Nonprofit Projects' + }); + }, + controlShortcuts: function controlShortcuts(req, res) { res.render('resources/control-shortcuts', { title: 'These Control Shortcuts will save you Hours' diff --git a/views/account/account.jade b/views/account/account.jade index 5e849b8278..2edf5fff54 100644 --- a/views/account/account.jade +++ b/views/account/account.jade @@ -317,9 +317,9 @@ block content a.close(href='#', data-dismiss='modal', aria-hidden='true') × h3 Are you really leaving us? .modal-body - p Pro Tip: If you tweet feedback to  + p Pro Tip: If you tweet feedback to  a(href="https://twitter.com/intent/tweet?text=Hey%20@freecodecamp") @FreeCodeCamp - | , we'll act quickly on it! + | , we'll act quickly on it! .modal-footer a.btn.btn-success.btn-block(href='#', data-dismiss='modal', aria-hidden='true') span.ion-happy diff --git a/views/account/email-signin.jade b/views/account/email-signin.jade index 860a9ce002..c4250fd697 100644 --- a/views/account/email-signin.jade +++ b/views/account/email-signin.jade @@ -14,7 +14,7 @@ block content button.btn.btn-primary(type='submit') span.ion-android-hand | Login - span    + span    a.btn.btn-info(href='/forgot') Forgot your password? br br diff --git a/views/bonfire/show.jade b/views/bonfire/show.jade index 77c5b61e0c..dda2c7712e 100644 --- a/views/bonfire/show.jade +++ b/views/bonfire/show.jade @@ -23,7 +23,7 @@ block content #testCreatePanel h1.text-center= name h2.text-center - .bonfire-flames Difficulty:  + .bonfire-flames Difficulty:  if (difficulty == "0") i.ion-ios-flame-outline i.ion-ios-flame-outline @@ -127,7 +127,7 @@ block content - if (points && points > 2) a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(target="_blank") - i.fa.fa-twitter   + i.fa.fa-twitter   = phrase - else a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress diff --git a/views/challenges/show.jade b/views/challenges/show.jade index becbc7cdb8..a0c81d2a7b 100644 --- a/views/challenges/show.jade +++ b/views/challenges/show.jade @@ -32,7 +32,7 @@ block content a.animated.fadeIn.btn.btn-lg.btn-primary.btn-block.next-challenge-button(name='_csrf', value=_csrf, aria-hidden='true') Take me to my next challenge - if (points && points > 2) a.animated.fadeIn.btn.btn-lg.btn-block.btn-twitter(target="_blank") - i.fa.fa-twitter   + i.fa.fa-twitter   = phrase - else a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress diff --git a/views/contact/nonprofits.jade b/views/contact/nonprofits.jade index 4d3115c15c..0a912839d4 100644 --- a/views/contact/nonprofits.jade +++ b/views/contact/nonprofits.jade @@ -4,7 +4,7 @@ block content .jumbotron h1.animated.lightSpeedIn.text-center.hug-top We'll code software solutions for your nonprofit, for free! .animated.fadeIn.delay-2.landing-panel-body - h3 Our students will help you build that donor tracking system, community message board, or whatever your organization needs,  + h3 Our students will help you build that donor tracking system, community message board, or whatever your organization needs,  strong at no cost. h3 In exchange, we ask only that you: h4 diff --git a/views/coursewares/showJS.jade b/views/coursewares/showJS.jade index a8183dae10..66ff8fed25 100644 --- a/views/coursewares/showJS.jade +++ b/views/coursewares/showJS.jade @@ -68,7 +68,7 @@ block content 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%20Bonfire:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{dashedName}&hashtags=LearnToCode, JavaScript" target="_blank") - i.fa.fa-twitter   + i.fa.fa-twitter   = phrase - else a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress \ No newline at end of file diff --git a/views/coursewares/showVideo.jade b/views/coursewares/showVideo.jade index 88099ddc42..c042cc749e 100644 --- a/views/coursewares/showVideo.jade +++ b/views/coursewares/showVideo.jade @@ -39,7 +39,7 @@ block content 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%20Bonfire:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/bonfires/#{dashedName}&hashtags=LearnToCode, JavaScript" target="_blank") - i.fa.fa-twitter   + i.fa.fa-twitter   = phrase - else a.animated.fadeIn.btn.btn-lg.signup-btn.btn-block(href='/login') Sign in so you can save your progress diff --git a/views/home.jade b/views/home.jade index 69d0196c0a..035e33483f 100644 --- a/views/home.jade +++ b/views/home.jade @@ -74,15 +74,15 @@ block content h2 Why you should join our community right now: h3.col-xs-offset-0.col-sm-offset-1 ul.text-left - li.ion-code   We're thousands of professionals, all learning to code together - li.ion-code   We're building projects for dozens of nonprofits - li.ion-code   Our community is 100% free and open source - li.ion-code   You'll learn Full Stack JavaScript and become a Software Engineer - li.ion-code   You'll work through our focused, interactive courses and tutorials - li.ion-code   You'll learn to code at your own pace, in your browser or on your phone - li.ion-code   You'll become qualified for thousands of jobs currently going unfilled - li.ion-code   You can get help in real time from our community chat rooms and forum - li.ion-code   We all share one common goal: to boost our careers with code + li.ion-code   We're thousands of professionals, all learning to code together + li.ion-code   We're building projects for dozens of nonprofits + li.ion-code   Our community is 100% free and open source + li.ion-code   You'll learn Full Stack JavaScript and become a Software Engineer + li.ion-code   You'll work through our focused, interactive courses and tutorials + li.ion-code   You'll learn to code at your own pace, in your browser or on your phone + li.ion-code   You'll become qualified for thousands of jobs currently going unfilled + li.ion-code   You can get help in real time from our community chat rooms and forum + li.ion-code   We all share one common goal: to boost our careers with code .big-break a.btn.btn-cta.signup-btn(href="/login") Learn to code today (it's free) script. diff --git a/views/partials/bonfires.jade b/views/partials/bonfires.jade index cabd9ecf93..e371443a16 100644 --- a/views/partials/bonfires.jade +++ b/views/partials/bonfires.jade @@ -3,5 +3,5 @@ h3 for bonfire in bonfires li a(href="/bonfires/#{bonfire.bonfireNumber}", class="#{ (cc && cc[bonfire.bonfireNumber] > 0) ? 'strikethrough' : '' }") #{bonfire.name} - |   (Level #{bonfire.difficulty}) + |   (Level #{bonfire.difficulty}) a.btn.btn-lg.btn-primary.btn-block(href="/done-with-first-100-hours", class="#{ ((cc && cc[53] === 0) || (!cc)) ? 'disabled' : '' }") I'm done with all the challenges! \ No newline at end of file diff --git a/views/partials/challenges.jade b/views/partials/challenges.jade index 1257d4b388..0c959ff122 100644 --- a/views/partials/challenges.jade +++ b/views/partials/challenges.jade @@ -3,6 +3,6 @@ h3 for challenge in challenges li a(href="/challenges/#{challenge.challengeNumber}", class="#{ (cc && cc[challenge.challengeNumber] > 0) ? 'strikethrough' : '' }") #{challenge.name} - |   (#{challenge.time} mins) + |   (#{challenge.time} mins) a.btn.btn-lg.btn-primary.btn-block(href="/done-with-first-100-hours", class="#{ ((points && points < 54) || (!points)) ? 'disabled' : '' }") I'm done with all the challenges! \ No newline at end of file diff --git a/views/partials/footer.jade b/views/partials/footer.jade index 1a6f65b4da..8be0eefa28 100644 --- a/views/partials/footer.jade +++ b/views/partials/footer.jade @@ -1,11 +1,11 @@ .fcc-footer .col-xs-12.hidden-xs.hidden-sm - a.ion-speakerphone(href='http://blog.freecodecamp.com', target='_blank')  Blog   - a.ion-social-twitch-outline(href="http://www.twitch.tv/freecodecamp", target='_blank')  Twitch   - a.ion-social-github(href="http://github.com/freecodecamp", target='_blank')  Github   - a.ion-social-twitter(href="http://twitter.com/freecodecamp", target='_blank')  Twitter   - a.ion-information-circled(href="/learn-to-code")  About   - a.ion-locked(href="/privacy")  Privacy   + a.ion-speakerphone(href='http://blog.freecodecamp.com', target='_blank')  Blog   + a.ion-social-twitch-outline(href="http://www.twitch.tv/freecodecamp", target='_blank')  Twitch   + a.ion-social-github(href="http://github.com/freecodecamp", target='_blank')  Github   + a.ion-social-twitter(href="http://twitter.com/freecodecamp", target='_blank')  Twitter   + a.ion-information-circled(href="/learn-to-code")  About   + a.ion-locked(href="/privacy")  Privacy   .col-xs-12.visible-xs.visible-sm a.ion-speakerphone(href='http://blog.freecodecamp.com', target='_blank') span.sr-only Free Code Camp's Blog diff --git a/views/partials/navbar.jade b/views/partials/navbar.jade index 16fd86ce30..c670c35e11 100644 --- a/views/partials/navbar.jade +++ b/views/partials/navbar.jade @@ -20,15 +20,15 @@ li a(href='/bonfires') Bonfires if !user - li       + li       li a.btn.signup-btn.signup-btn-nav(href='/login') Sign in else li if (user.profile.username) - a(href='/' + user.profile.username) [ #{user.points} ] + a(href='/' + user.profile.username) [ #{user.points} ] else - a(href='/account') [ #{user.points} ] + a(href='/account') [ #{user.points} ] .hidden-xs if user.profile.picture if (user.profile.username) diff --git a/views/resources/chat.jade b/views/resources/chat.jade index 9647804dea..e8dff714b0 100644 --- a/views/resources/chat.jade +++ b/views/resources/chat.jade @@ -2,18 +2,18 @@ extends ../layout-wide block content h3 ol.col-md-offset-2 - li Create a GitHub Account  + li Create a GitHub Account  a(href="http://github.com/join", target='_blank') here | . - li Download the chat room app on   + li Download the chat room app on   a(href="https://update.gitter.im/win/latest") Windows - | ,  + | ,  a(href="https://update.gitter.im/osx/latest") Mac - | ,  + | ,  a(href="http://appstore.com/gitter") iPhone - | ,  or   + | ,  or   a(href="https://play.google.com/store/apps/details?id=im.gitter.gitter&hl=en_GB") Android - |  , or go   + |  , or go   a(href="http://chat.freecodecamp.com") here | . li Keep the chat room open while you code so that you can meet friends and ask for help. diff --git a/views/resources/deploy-a-website.jade b/views/resources/deploy-a-website.jade index d9c281eb8a..bd5a156fb4 100644 --- a/views/resources/deploy-a-website.jade +++ b/views/resources/deploy-a-website.jade @@ -11,15 +11,15 @@ block content ul li a(href='http://www.atom.io' target='_blank') http://www.atom.io - |   - free text editor + |   - free text editor li a(href='http://www.startbootstrap.com' target='_blank') http://www.startbootstrap.com - |   - free responsive (Bootstrap) templates + |   - free responsive (Bootstrap) templates li a(href='http://www.powr.io' target='_blank') http://www.powr.io - |   - great plugins + |   - great plugins li a(href='http://www.bitballoon.com' target='_blank') http://www.bitballoon.com - |   - drag-and-drop deployment + |   - drag-and-drop deployment a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free) br diff --git a/views/resources/guide-to-our-nonprofit-projects.jade b/views/resources/guide-to-our-nonprofit-projects.jade new file mode 100644 index 0000000000..a0a3d39569 --- /dev/null +++ b/views/resources/guide-to-our-nonprofit-projects.jade @@ -0,0 +1,96 @@ +extends ../layout +block content + .jumbotron + h1.hug-top.text-center A Guide to our Nonprofit Projects + br + p Building nonprofit projects is the main way that our campers learn full stack JavaScript and agile software development. Once you complete the Free Code Camp challenges and Bonfire challenges, you'll begin this process. + p Once you've finished all the challenges, click the "I'm done with all the challenges" button, which will become enabled. We will prompt you for your email address, then give you further instructions on our   + a(href="http://www.freecodecamp.com/nonprofit-project-instructions") Nonprofit Projects Instructions page + | . + p We will add you to our   + a(href="https://trello.com/b/BA3xVpz9/nonprofit-projects") Nonprofit Project Trello board + | . + h2 Starting with the end in mind + p Our goal at Free Code Camp is to help you land a job as a junior software developer (or, if you prefer, a 'pivot job' that leads your current career in a more technical direction). + p You'll continue to work on nonprofit projects until you've built a sufficiently impressive portfolio and references to start your job search. Your portfolio will ultimately have three to five nonprofit projects. We estimate that the 900 hours of nonprofit projects you're going to complete, in addition to the 100 hours of challenges you've already completed, will be enough to qualify you for your first coding job. This will produce a much broader portfolio than a traditional coding bootcamp, which generally only has one or two capstone projects. + h2 Choosing your first Nonprofit Project + p We've categorized all the nonprofit projects by estimated time investment per camper: 100 hours, 200 hours, and 300 hours. These are only rough estimates. + p Example: if you and the camper you're paired up with (your pair) each stated you could work 20 hours per week (on the   + a(href="http://goo.gl/forms/f61dLt67t8") form you filled out + | ). If the project is a 100 hour per camper project, you should be able to complete it in about 5 weeks. + p Our team of nonprofit project camp counselors will match you and your pair based on: + ol + li Your estimated time commitment (10, 20 or 40 hours per week) + li Your time zone + li The nonprofit projects you've chosen + li Prior coding experience (we'd like both campers to be able to contribute equally) + p We won't take age or gender into account. This will provide you with valuable experience in meshing with diverse teams, which is a reality of the contemporary workplace. + p You'll only work on one project at a time. Once you start a nonprofit project, we'll remove you from all other nonprofit project Trello cards. There's a good chance those projects will no longer be available when you finish your current project, anyway. Don't worry, though - we get new nonprofit project requests every day, so there will be plenty more projects for you to consider after you finish your current one. + h2 Finalizing the Project + p Before you can start working on the project, our team of Nonprofit Project Coordinators will go through the following process: + ol + li We'll wait until there are two campers who have chosen the same project and look like they're a good match for one another based on the factors mentioned above. + li We'll call the stakeholder to confirm once again that he or she agrees with our   + a(href="freecodecamp.com/nonprofits") terms   + | and has signed our   + a(href="http://goo.gl/forms/0YKkd9bpcR") Nonprofit Project Stakeholder Pledge + | . + li We'll set an initial meeting with representatives from Free Code Camp, the two campers, and the stakeholder. + li If the stakeholder and both campers shows up promptly, and seem enthusiastic and professional, we'll start the project. + p This lengthy process serves an important purpose: it reduces the likelihood that any of our campers or stakeholders will waste their precious time. + h2 Nonprofit Stakeholders + p Each nonprofit project was submitted by a nonprofit. A representative from this nonprofit has agreed to serve as a "stakeholder" - an authorative person who understands the organization and its needs for this particular project. + p Stakeholders have a deep understanding of their organizations' needs. Campers will work with them to figure out the best solutions to these needs. + p When you and your pair first speak with your nonprofit stakeholder, you'll: + ul + li talk at length to better understand their needs. + li create a new Trello board and use it to prioritize what needs to be built. + li and establish deadlines based on your weekly time commitment, and how long you think each task will take. + p It's notoriously difficult to estimate how long building software projects will take, so feel free to ask camp counselors for help. + p You'll continue to meet with your stakeholder regularly throughout this process. + p Ultimately, the project will be considered complete once both the stakeholder's needs have been met, and you and your pair are happy with the project. Then you can add it to your portfolio! + h2 Working with your Pair + p You and your pair will pair program (code together on the same computer virtually) about half of the time, and work independently the other half of the time. + p Here are our recommended ways of collaborating: + ul + li • Gitter has robust private messaging functionality. It's the main way our team communicates, and we recommend it over email. + li • Trello is great for managing projects. Work with your stakeholder to create Trello cards, and update these cards regularly as you make progress on them. + li • Screen Hero or Team Viewer - These are the ideal way to pair program. Tools like TMUX are good, but difficult to use. We discourage you from using screen sharing tools where only one person has control of the keyboard and mouse - that isn't real pair programming. + li • Write clear and readable code, commit messages, branch names, and pull request messages. + h2 Setting up your Development Environment + p We've created a custom virtual machine image with Ubuntu Linux, Git, Team Viewer, the MEAN Stack and all its dependencies. You can run this virtual image on any computer with at least 2 gigabytes of RAM and 16 gigabytes of hard drive space. + p The benefits of using this virtual machine are as follows: + ul + li • Everyone else on Free Code Camp is using this image, so we can all help you troubleshoot various problems that may arise. + li • When you pair program, you and your pair will have the exact same environment, which means you will both feel comfortable on each other's machines. + li • You can install the image on any computer without worrying about messing up the computer's original data or configuration. + li • Even if you end up using Windows or Mac OSX for development later, your server will almost certainly run Linux, so it's worth getting used to Linux. + li • Even experienced developers encounter hangups when setting up a development environment. This virtual machine image will remove this tedious process. + p Install a bit torrent client, then   + a(href="http://mgnet.me/ZOQk0rd") download our virtual machine image + | . + p Please note that even though Bit Torrent is often used to download content illegally, all the content on our image is open source and perfectly legal to redistribute. + p Once you've downloaded the file,   + a(href="https://www.virtualbox.org/wiki/Downloads") download VirtualBox   + | and follow   + a(href="http://techathlon.com/how-to-run-a-vmdk-file-in-oracle-virtualbox/") this tutorial   + | to open the image in VirtualBox. You'll want to assign the virtual machine at least two gigabytes of ram. + p Now you have your own Linux development environment. You can shut it down when you're not using it and it will save its state. Please continue to seed the file in bit torrent so that other campers can download it as well. Enjoy! + h2 Hosting Apps + p Unless your stakeholder has an existing modern host (AWS, Digital Ocean), you'll need to transition them over to a new platform. We believe Heroku is the best choice for a vast majority of web projects. It's free, easy to use, and has both browser and command line interfaces. It's owned by Salesforce and used by a ton of companies, so it's accountable and unlikely to go away. + p If you need help convincing your stakeholder that Heroku is the ideal platform, we'll be happy to talk with them. + h2 Maintaining Apps + p Once you complete a nonprofit project, your obligation to its stakeholder is finished. You goal is to leave behind a well documented solution that can be easily maintained by a contract JavaScript developer (or even a less-technical "super user"). + p While you will no longer need to help with feature development, we encourage you to consider helping your stakeholder with occasional patches down the road. After all, this project will be an important piece of your portfolio, and you'll want it to remain in good shape for curious future employers. + h2 Office Hours + p Quincy Larson and/or Michael Johnson will be in the   + a(href="https://gitter.im/FreeCodeCamp/NonprofitProjects") Gitter Nonprofit Project Chatroom   + | every Monday and Thursday from 9 - 10 p.m. EST. + p Our goal is to make the discussion as public as possible so all our campers can benefit from each camper’s questions. + p If necessary, we can also hop on Screen Hero with you to help you with issues more specific to your project. + h2 Pledging to finish the project + p Your nonprofit stakeholder, your pair, and the volunteer camp counselor team are all counting on you to finish your nonprofit project. If you walk away from an unfinished nonprofit project, you'll become ineligible to ever be assigned another one. + p To confirm that you understand the seriousness of this commitment, we require that all campers   + a(href="http://goo.gl/forms/ZMn96z2QqY") sign this pledge   + | before starting on their nonprofit projects. + p There will likely be times of confusion or frustration. This is normal in software development. The most important thing is that you do not give up and instead persevere through these setbacks. As Steve Jobs famously said, "Real artists ship." And you are going to ship one successful nonprofit project after another until you feel ready to take the next step in your promising career. \ No newline at end of file diff --git a/views/resources/install-screenhero.jade b/views/resources/install-screenhero.jade index 5676bdd923..cf0881ada2 100644 --- a/views/resources/install-screenhero.jade +++ b/views/resources/install-screenhero.jade @@ -6,8 +6,8 @@ block content a(href="http://links.screenhero.com/e/c/eyJlbWFpbF9pZCI6Ik1qQTNNem9XQkNJQ1pBQUNjd0FYQVZrVEdnRkxNamtfX0JWZEdGVEpSZkVCWlRwbFpXRTBNamM0WVMxaE56SmlMVEV4WlRRdE9HUXpZUzFpWXpVNE1HRTJNalkxTldNNk1UUTJNVEEyQUE9PSIsInBvc2l0aW9uIjowLCJocmVmIjoiaHR0cDovL2RsLnNjcmVlbmhlcm8uY29tL3NtYXJ0ZG93bmxvYWQvZklYQU1UUUJBTEtQQkhQTC9TY3JlZW5oZXJvLnppcD9zb3VyY2U9d2ViIn0=") Download for Mac h2 a(href="http://links.screenhero.com/e/c/eyJlbWFpbF9pZCI6Ik1qQTNNem9XQkNJQ1pBQUNjd0FYQVZrVEdnRkxNamtfX0JWZEdGVEpSZkVCWlRwbFpXRTBNamM0WVMxaE56SmlMVEV4WlRRdE9HUXpZUzFpWXpVNE1HRTJNalkxTldNNk1UUTJNVEEyQUE9PSIsInBvc2l0aW9uIjoxLCJocmVmIjoiaHR0cDovL2RsLnNjcmVlbmhlcm8uY29tL3NtYXJ0ZG93bmxvYWQvZklYQU1UUUJBTEtQQkhQTC9TY3JlZW5oZXJvLXNldHVwLmV4ZSJ9") Download for Windows - p You can learn more about using Screen Hero by taking   + p You can learn more about using Screen Hero by taking   a(href="http://www.freecodecamp.com/challenges/34") Challenge 34. - p Screen Hero was recently acquired by a collaboration tool called Slack. It's still available and free, but will go away in the indefinite future. Discuss alternatives on our   + p Screen Hero was recently acquired by a collaboration tool called Slack. It's still available and free, but will go away in the indefinite future. Discuss alternatives on our   a(href="http://forum.freecodecamp.com/t/replacing-screen-hero/992") Screen Hero replacement thread | . \ No newline at end of file diff --git a/views/resources/live-pair-programming.jade b/views/resources/live-pair-programming.jade index 46ba914596..ba2a4c0094 100644 --- a/views/resources/live-pair-programming.jade +++ b/views/resources/live-pair-programming.jade @@ -7,7 +7,7 @@ block content h2#next-session - h2 Watch the live stream below or on our   + h2 Watch the live stream below or on our   a(href="http://twitch.tv/freecodecamp", target='_blank') Twitch.tv channel | . .row @@ -25,23 +25,23 @@ block content .col-xs-12 .embed-responsive.embed-responsive-16by9.big-break iframe.embed-responsive-item(src='//www.youtube.com/embed/_BErpDdmBOw') - h3.wrappable link:   + h3.wrappable link:   a(href="http://www.youtube.com/watch/_BErpDdmBOw") http://www.youtube.com/watch/_BErpDdmBOw .embed-responsive.embed-responsive-16by9.big-break iframe.embed-responsive-item(src='//www.youtube.com/embed/Fn9HMn79KH0') - h3.wrappable link:   + h3.wrappable link:   a(href="http://www.youtube.com/watch/Fn9HMn79KH0") http://www.youtube.com/watch/Fn9HMn79KH0 .embed-responsive.embed-responsive-16by9.big-break iframe.embed-responsive-item(src='//www.youtube.com/embed/S7iRBZJwOAs') - h3.wrappable link:   + h3.wrappable link:   a(href="http://www.youtube.com/watch/S7iRBZJwOAs") http://www.youtube.com/watch/S7iRBZJwOAs .embed-responsive.embed-responsive-16by9.big-break iframe.embed-responsive-item(src='//www.youtube.com/embed/BHNRg39ZblE') - h3.wrappable link:   + h3.wrappable link:   a(href="http://www.youtube.com/watch/BHNRg39ZblE") http://www.youtube.com/watch/BHNRg39ZblE .embed-responsive.embed-responsive-16by9.big-break iframe.embed-responsive-item(src='//www.youtube.com/embed/YDfkHlDmehA') - h3.wrappable link:   + h3.wrappable link:   a(href="http://www.youtube.com/watch/YDfkHlDmehA") http://www.youtube.com/watch/YDfkHlDmehA h3 Got 3 minutes? Learn to code with us! a.btn.btn-cta.signup-btn.btn-primary(href="/login") Start learning to code (it's free) diff --git a/views/resources/nonprofit-project-instructions.jade b/views/resources/nonprofit-project-instructions.jade index 8c88acf588..3043e41668 100644 --- a/views/resources/nonprofit-project-instructions.jade +++ b/views/resources/nonprofit-project-instructions.jade @@ -8,16 +8,16 @@ block content h3 Please do the following immediately: h4 ol - li Complete this form:   + li Complete this form:   a(href="http://goo.gl/forms/f61dLt67t8" target="_blank") http://goo.gl/forms/f61dLt67t8 | . - li Read this document, which will answer many questions you may have about our nonprofit projects:   - a(href="http://forum.freecodecamp.com/t/an-introduction-to-our-nonprofit-projects/856" target="_blank") http://forum.freecodecamp.com/t/an-introduction-to-our-nonprofit-projects/856 + li Read this document, which will answer many questions you may have about our nonprofit projects:   + a(href="/guide-to-our-nonprofit-projects" target="_blank") http://freecodecamp.com/guide-to-our-nonprofit-projects | . li We'll send you an invite to our Nonprofit Projects Trello board. Once we do, go there and add yourself to at least 3 nonprofit projects that interest you. - li Finish any unfinished Bonfire challenges. These challenges serve as the Free Code Camp "exit test". You must complete these before you can start working on nonprofit projects. If you completed CoderByte or CodeWars challenges instead of Bonfire, email us and we'll take a look:  + li Finish any unfinished Bonfire challenges. These challenges serve as the Free Code Camp "exit test". You must complete these before you can start working on nonprofit projects. If you completed CoderByte or CodeWars challenges instead of Bonfire, email us and we'll take a look:  a(href="mailto:team@freecodecamp.com") team@freecodecamp.com | . - h4 Please email us if you have further questions:   + h4 Please email us if you have further questions:   a(href="mailto:team@freecodecamp.com") team@freecodecamp.com | . \ No newline at end of file diff --git a/views/resources/privacy.jade b/views/resources/privacy.jade index 896c2437b5..54278c5b57 100644 --- a/views/resources/privacy.jade +++ b/views/resources/privacy.jade @@ -26,7 +26,7 @@ block content

Contacting Us

If you have questions about Free Code Camp, or to correct, update, or remove personally identifiable information, please email us at team@freecodecamp.com.

Links to Other Web sites

-

The Free Code Camp sites each contain links to other Web sites. Free Code Camp is not responsible for the privacy practices or content of these third-party Web sites. We urge all FreeCodeCamp.com visitors to follow safe Internet practices: Do not supply Personally Identifiable Information to these Web sites unless you have verified their security and privacy policies.

+

Free Code Camp's sites each contain links to other Web sites. Free Code Camp is not responsible for the privacy practices or content of these third-party Web sites. We urge all FreeCodeCamp.com visitors to follow safe Internet practices: Do not supply Personally Identifiable Information to these Web sites unless you have verified their security and privacy policies.

Data Retention

We retain your information for as long as necessary to permit us to use it for the purposes that we have communicated to you and comply with applicable law or regulations.

Business Transfers

diff --git a/views/stories/news-nav.jade b/views/stories/news-nav.jade index 0980167c31..15d6f33799 100644 --- a/views/stories/news-nav.jade +++ b/views/stories/news-nav.jade @@ -17,19 +17,19 @@ .btn-group.input-group.btn-group-justified(data-toggle='buttons') label.btn.btn-primary.btn-big(class="#{ page === 'hot' ? 'active' : '' }") input#option1(href='/stories/hot', type='radio', name='options') - i.ion-ios-pulse-strong   + i.ion-ios-pulse-strong   | Hot label.btn.btn-primary.btn-big(class="#{ page === 'recent' ? 'active' : '' }") input#option2(href='/stories/recent', type='radio', name='options') - i.ion-ios-clock   + i.ion-ios-clock   | New label.btn.btn-primary.btn-big(class="#{ page === 'submit' ? 'active' : page === 'storySubmission' ? 'active' : '' }") input#option3(href='/stories/submit', type='radio', name='options') - i.ion-plus-circled   + i.ion-plus-circled   | Submit label.btn.btn-primary.btn-big(class="#{ page === 'search' ? 'active' : '' }") input#option4(href='/stories/search', type='radio', name='options') - i.ion-search   + i.ion-search   | Search script. diff --git a/views/stories/show.jade b/views/stories/show.jade index 59764476c1..68fde58036 100644 --- a/views/stories/show.jade +++ b/views/stories/show.jade @@ -25,7 +25,7 @@ h6 .col-xs-12.negative-15 span Posted #{timeAgo} - span  by  + span  by  a(href="/" + author.username) @#{author.username} .col-xs-10.col-xs-offset-1 p= description From de91be44dace653329b336e419d4d37d4e15e6b2 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sun, 8 Mar 2015 18:48:20 +0900 Subject: [PATCH 46/49] comment submit time bug fixed --- controllers/story.js | 6 +++-- models/Comment.js | 6 ++--- public/css/main.less | 19 ++++++++++---- views/stories/comments.jade | 49 +++++++++++++++++-------------------- 4 files changed, 43 insertions(+), 37 deletions(-) diff --git a/controllers/story.js b/controllers/story.js index b3eab98834..c88df28fdb 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -269,7 +269,8 @@ exports.commentSubmit = function(req, res, next) { upvotes: 0, author: data.author, comments: [], - topLevel: true + topLevel: true, + commentOn: Date.now() }); commentSave(comment, Story, res); }; @@ -285,7 +286,8 @@ exports.commentOnCommentSubmit = function(req, res, next) { upvotes: 0, author: data.author, comments: [], - topLevel: false + topLevel: false, + commentOn: Date.now() }); commentSave(comment, Comment, res); }; diff --git a/models/Comment.js b/models/Comment.js index 89d1804654..a3a35fe6fd 100644 --- a/models/Comment.js +++ b/models/Comment.js @@ -23,9 +23,9 @@ var commentSchema = new mongoose.Schema({ type: Array, default: [] }, - toplevel: { - type: Boolean, - default: false + commentOn: { + type: Number, + default: Date.now() } }); diff --git a/public/css/main.less b/public/css/main.less index c12a897789..f65df91a75 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -175,6 +175,14 @@ ul { margin-top: 5px; } +.positive-10 { + margin-top: 10px; +} + +.positive-15 { + margin-top: 15px; +} + .negative-45 { margin-top: -45px; margin-bottom: -45px; @@ -815,13 +823,14 @@ iframe.iphone { height: 50px; } -.comment-wrapper { - position: relative; +//.media ~ .media .media-body-wrapper:nth-child(odd) { +// background-color: #e5e5e5; +//} + +.media-body-wrapper:nth-child(odd){ + background-color: #e5e5e5; } -.comment + .comment-wrapper { - left: 3%; -} //uncomment this to see the dimensions of all elements outlined in red //* { diff --git a/views/stories/comments.jade b/views/stories/comments.jade index b85cd7e7e0..1ca087a539 100644 --- a/views/stories/comments.jade +++ b/views/stories/comments.jade @@ -1,5 +1,5 @@ .text-left - div#comment-list.comment-list + div#comment-list script(src="https://cdn.jsdelivr.net/ramda/0.10.0/ramda.min.js") script(src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/moment.min.js") @@ -7,9 +7,9 @@ var sentinel = 0; var renderComments = function renderComments(comments, containerSelector, level) { var commentDetails; - var offSetClass = level ? 'col-xs-offset-' + level : ''; - var rowClass = level ? 'col-xs-' + (12 - level) : 'col-xs-12'; + var backgroundColorForCommentNestingLevel = level % 2 !== 0 ? '#e5e5e5' : '#e7e7e7imit T'; R.forEach(function displayComments(comment) { + $.ajax({ type: 'GET', url: '/stories/comments/' + comment, @@ -22,32 +22,27 @@ }, success: function (data, textStatus, xhr) { commentDetails = data; + console.log(commentDetails.commentOn); var div = document.createElement('div'); $(div) .html( - "
" + - "
" + - "" + - "
" + - "
" + - "
" + - "
" + - commentDetails.body + - "
" + - "
" + - "
" + - "
" + - "
" + - "commented " + moment(commentDetails.commentedOn).fromNow() + " by " + - "@" + commentDetails.author.username + " · " + - "Reply" + - "
" + - "
" + - "
" + - "
" + - "
" + '
' + + '
' + + '' + commentDetails.author.username + '' + + '
' + + '
' + + '
' + + '

' + commentDetails.body + '

' + + '
' + + "commented " + moment(commentDetails.commentOn).fromNow() + " by " + + "@" + commentDetails.author.username + " · " + + "Reply" + + '
' + + '
' + + '
' + + '
' ) - .addClass('comment-wrapper comment_' + commentDetails._id) + .addClass('comment-wrapper positive-10') .appendTo($(containerSelector)); sentinel += commentDetails.comments.length; @@ -68,8 +63,8 @@ "" + "
" ) - .addClass('col-xs-6 col-xs-offset-3') - .appendTo($(this).closest('.comment')); + .addClass('row') + .appendTo($(this).closest('.media-body-wrapper')); var submitCommentToCommentHandler = function submitCommentToCommentHandler() { $('#submit-comment-to-comment').unbind('click'); $.post('/stories/comment/' + commentId + '/comment', From 212e59e061e725cb11baa7ecf1f8f254bfd91b50 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sun, 8 Mar 2015 20:22:55 +0900 Subject: [PATCH 47/49] Minor view tweaking --- public/css/main.less | 11 ++++++++--- views/stories/comments.jade | 39 ++++++++++++++++++++++++++----------- views/stories/show.jade | 25 ++++++++++++------------ 3 files changed, 49 insertions(+), 26 deletions(-) diff --git a/public/css/main.less b/public/css/main.less index a253ec314c..4af818a206 100644 --- a/public/css/main.less +++ b/public/css/main.less @@ -178,7 +178,7 @@ ul { } .positive-10 { - margin-top: 10px; + padding-top: 10px; } .positive-15 { @@ -829,8 +829,13 @@ iframe.iphone { // background-color: #e5e5e5; //} -.media-body-wrapper:nth-child(odd){ - background-color: #e5e5e5; +.media-news { + width: 100%; +} + +.comment-to-comment-formgroup { + width: 50%; + height: 35px; } diff --git a/views/stories/comments.jade b/views/stories/comments.jade index 1ca087a539..83f9f9f75f 100644 --- a/views/stories/comments.jade +++ b/views/stories/comments.jade @@ -7,7 +7,7 @@ var sentinel = 0; var renderComments = function renderComments(comments, containerSelector, level) { var commentDetails; - var backgroundColorForCommentNestingLevel = level % 2 !== 0 ? '#e5e5e5' : '#e7e7e7imit T'; + var backgroundColorForCommentNestingLevel = level % 2 !== 0 ? 'odd' : 'even'; R.forEach(function displayComments(comment) { $.ajax({ @@ -26,18 +26,22 @@ var div = document.createElement('div'); $(div) .html( - '
' + + '
' + '
' + - '' + commentDetails.author.username + '' + + "" + + '' + commentDetails.author.username + '' + + '' + '
' + '
' + '
' + '

' + commentDetails.body + '

' + - '
' + - "commented " + moment(commentDetails.commentOn).fromNow() + " by " + - "@" + commentDetails.author.username + " · " + - "Reply" + - '
' + + '
' + + '
' + + "commented " + moment(commentDetails.commentOn).fromNow() + " by " + + "@" + commentDetails.author.username + " · " + + "Reply" + + '
' + + '
' + '
' + '
' + '
' @@ -55,16 +59,28 @@ if (!sentinel) { $('.comment-a-comment').on('click', 'a', function () { $(this).unbind('click'); + $('.comment-to-comment-formgroup').empty(); var div = document.createElement('div'); var commentId = $(this).attr('id'); $(div).html( - "
" + - "" + - "" + + "
" + + "" + + '
' + + '' + + "" + + '
' + + "
" ) .addClass('row') .appendTo($(this).closest('.media-body-wrapper')); + var text_max = 140; + $('#textarea_feedback').html(text_max + ' characters remaining'); + $('#comment-to-comment-textarea').keyup(function () { + var text_length = $('#comment-to-comment-textarea').val().length; + var text_remaining = text_max - text_length; + $('#textarea_feedback').html(text_remaining + ' characters remaining'); + }); var submitCommentToCommentHandler = function submitCommentToCommentHandler() { $('#submit-comment-to-comment').unbind('click'); $.post('/stories/comment/' + commentId + '/comment', @@ -99,6 +115,7 @@ } sentinel += comments.length; + renderComments(comments, $('#comment-list'), 0); diff --git a/views/stories/show.jade b/views/stories/show.jade index e09dbb5085..b6c99affef 100644 --- a/views/stories/show.jade +++ b/views/stories/show.jade @@ -7,8 +7,8 @@ var user = !{JSON.stringify(user)}; .spacer - .row.col-xs-12 - h3.row.text-left.negative-10 + h3.row.col-xs-12 + .row.text-left.negative-10 .col-xs-3.col-sm-1.text-center .row.negative-5 .col-xs-12.big-ion-up-arrow @@ -17,21 +17,22 @@ h3.story-up-votes span#storyRank= rank .col-xs-2.col-sm-1 - img(src="#{author.picture}", class='img-news') + a(href="/" + author.username) + img(src="#{author.picture}", class='img-news') .col-xs-7.col-sm-10 + .col-xs-12.negative-28 a(href="#{link}") h3= title h6 - .col-xs-12.negative-15 - span Posted #{timeAgo} - span  by  - a(href="/" + author.username) - | @#{author.username} - |  Â·  - a#reply-to-main-post Reply - .col-xs-10.col-xs-offset-1 - p= description + .col-xs-12.negative-28 + h4= description + .negative-5 + span Posted #{timeAgo} + span  by  + a(href="/" + author.username)@#{author.username} + |  Â·  + a#reply-to-main-post Reply .col-xs-12#reply-area From bd81c678f9a8fcd7028fb945bcf6bc75fcd96418 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sun, 8 Mar 2015 20:36:13 +0900 Subject: [PATCH 48/49] Add story limiting for hotStories, not working on recentStories --- controllers/story.js | 4 +- seed_data/stories.json | 3081 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 3082 insertions(+), 3 deletions(-) diff --git a/controllers/story.js b/controllers/story.js index c88df28fdb..775077a551 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -11,7 +11,7 @@ var R = require('ramda'), User = require('./../models/User'); exports.hotJSON = function(req, res, next) { - var story = Story.find({}).sort({'rank': -1, 'timePosted': -1}); + var story = Story.find({}).sort({'rank': -1, 'timePosted': -1}).limit(100); story.exec(function(err, stories) { if (err) { throw err; @@ -21,7 +21,7 @@ exports.hotJSON = function(req, res, next) { }; exports.recentJSON = function(req, res, next) { - var story = Story.find({}).sort({'timePosted': -1}); + var story = Story.find({}).sort({'timePosted': -1}).limit(100); story.exec(function(err, stories) { if (err) { throw err; diff --git a/seed_data/stories.json b/seed_data/stories.json index c44dc44f37..b16ac0d19b 100644 --- a/seed_data/stories.json +++ b/seed_data/stories.json @@ -1,3 +1,3082 @@ [ - + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + }, + { + "headline": "Learn to Code and Become a Software Engineer | Free Code Camp", + "link": "http://www.freecodecamp.com", + "description": "The website we're building these \"forums\" for!", + "author": { + "username": "terakilobyte", + "userId": "54f6262394f355ce1d969d9f", + "picture": "https://pbs.twimg.com/profile_images/2563218307/67rpczkpeyo1xem5vto3.gif" + }, + "storyLink": "learn to code and become a software engineer free code camp", + "image": "", + "comments": [ + ], + "upVotes": [ + { + "upVotedByUsername": "terakilobyte", + "upVotedBy": "54f6262394f355ce1d969d9f" + } + ], + "rank": 1, + "timePosted": 1425806926395.0 + } ] \ No newline at end of file From 38dc4378df415765610d461020b03777abb9f20d Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Mon, 9 Mar 2015 06:09:51 +0900 Subject: [PATCH 49/49] Add ranking algorithm to decay posts over time, add default image to user instead of gravatar --- controllers/story.js | 32 +++++++++++++++++++++++++++++--- controllers/user.js | 2 +- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/controllers/story.js b/controllers/story.js index 775077a551..78ceda95d4 100644 --- a/controllers/story.js +++ b/controllers/story.js @@ -10,13 +10,38 @@ var R = require('ramda'), secrets = require('../config/secrets'), User = require('./../models/User'); +function hotRank(timeValue, rank, headline) { + /* + * Hotness ranking algorithm: http://amix.dk/blog/post/19588 + * tMS = postedOnDate - foundationTime; + * Ranking... + * f(ts, 1, rank) = log(10)z + (ts)/45000; + */ + var hotness; + var z = Math.log(rank) / Math.log(10); + hotness = z + (timeValue / 45000000); + return hotness; + +} + exports.hotJSON = function(req, res, next) { - var story = Story.find({}).sort({'rank': -1, 'timePosted': -1}).limit(100); + var story = Story.find({}).sort({'timePosted': -1}).limit(1000); story.exec(function(err, stories) { if (err) { throw err; } - res.json(stories); + + var foundationDate = 1413298800000; + + var sliceVal = stories.length >= 100 ? 100 : stories.length; + var rankedStories = stories; + return res.json(rankedStories.map(function(elem) { + return elem; + }).sort(function(a, b) { + debug('a rank and b rank', hotRank(a.timePosted - foundationDate, a.rank, a.headline), hotRank(b.timePosted - foundationDate, b.rank, b.headline)); + return hotRank(b.timePosted - foundationDate, b.rank, b.headline) - hotRank(a.timePosted - foundationDate, a.rank, a.headline); + }).slice(0, sliceVal)); + }); }; @@ -140,7 +165,7 @@ exports.getStories = function(req, res, next) { } } }).toArray(function(err, items) { - if (items.length !== 0) { + if (items !== null && items.length !== 0) { return res.json(items); } return res.status(404); @@ -162,6 +187,7 @@ exports.upvote = function(req, res, next) { upVotedByUsername: data.upVoter.profile.username } ); + story.markModified('rank'); story.save(); return res.send(story); }); diff --git a/controllers/user.js b/controllers/user.js index c0da54bf47..569f418e33 100644 --- a/controllers/user.js +++ b/controllers/user.js @@ -331,7 +331,7 @@ exports.postUpdateProfile = function(req, res, next) { user.profile.codepenProfile = req.body.codepenProfile.trim() || ''; user.profile.twitterHandle = req.body.twitterHandle.trim() || ''; user.profile.bio = req.body.bio.trim() || ''; - user.profile.picture = req.body.picture.trim() || ''; + user.profile.picture = req.body.picture.trim() || 'https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-180x180.png'; user.portfolio.website1Title = req.body.website1Title.trim() || ''; user.portfolio.website1Link = req.body.website1Link.trim() || ''; user.portfolio.website1Image = req.body.website1Image.trim() || '';