removes comments

moves upvote to main page
add more info page
closes #955
This commit is contained in:
Berkeley Martinez
2015-07-31 19:51:21 -07:00
parent d52525f1f4
commit ee8776feb3
10 changed files with 48 additions and 522 deletions

View File

@ -277,34 +277,32 @@ $(document).ready(function() {
$('#long-instructions').hide();
});
var upvoteHandler = function () {
var id = storyId;
$('#upvote').unbind('click');
function upvoteHandler(e) {
e.preventDefault();
var upvoteBtn = this;
var id = upvoteBtn.id;
var upVotes = $(upvoteBtn).data().upVotes;
var username = typeof username !== 'undefined' ? username : '';
var alreadyUpvoted = false;
for (var i = 0; i < upVotes.length; i++) {
if (upVotes[i].upVotedBy === B3BA669EC5C1DD70FB478221E067A7E1B686929C569F5E73561B69C8F42129B) {
if (upVotes[i].upVotedBy === username) {
alreadyUpvoted = true;
break;
}
}
if (!alreadyUpvoted) {
$.post('/stories/upvote',
{
data: {
id: id
}
$.post('/stories/upvote', { id: id })
.fail(function(xhr, textStatus, errorThrown) {
$(upvoteBtn).bind('click', upvoteHandler);
})
.fail(function (xhr, textStatus, errorThrown) {
$('#upvote').bind('click', upvoteHandler);
})
.done(function (data, textStatus, xhr) {
$('#upvote').text('Upvoted!').addClass('disabled');
.done(function(data, textStatus, xhr) {
$(upvoteBtn).text('Upvoted!').addClass('disabled');
$('#storyRank').text(data.rank + " points");
});
}
};
$('#upvote').on('click', upvoteHandler);
$('#story-list').on('click', 'button.btn-upvote', upvoteHandler);
var storySubmitButtonHandler = function storySubmitButtonHandler() {
@ -322,7 +320,6 @@ $(document).ready(function() {
description: description,
storyMetaDescription: storyMetaDescription,
rank: 1,
comments: [],
image: storyImage
}
})
@ -336,29 +333,6 @@ $(document).ready(function() {
$('#story-submit').on('click', storySubmitButtonHandler);
var commentSubmitButtonHandler = function commentSubmitButtonHandler() {
$('#comment-button').unbind('click');
var data = $('#comment-box').val();
$('#comment-button').attr('disabled', 'disabled');
$.post('/stories/comment/',
{
data: {
associatedPost: storyId,
originalStoryLink: originalStoryLink,
originalStoryAuthorEmail: originalStoryAuthorEmail,
body: data
}
})
.fail(function (xhr, textStatus, errorThrown) {
$('#comment-button').attr('disabled', false);
$('#comment-button').bind('click', commentSubmitButtonHandler);
})
.done(function (data, textStatus, xhr) {
window.location.reload();
});
};
//fakeiphone positioning hotfix
if($('.iphone-position').html() !==undefined || $('.iphone').html() !== undefined){
var startIphonePosition = parseInt($('.iphone-position').css('top').replace('px', ''));
@ -406,8 +380,6 @@ $(document).ready(function() {
});
}
}
$('#comment-button').on('click', commentSubmitButtonHandler);
});
var profileValidation = angular.module('profileValidation',

View File

@ -1,5 +1,4 @@
var Rx = require('rx'),
nodemailer = require('nodemailer'),
assign = require('object.assign'),
sanitizeHtml = require('sanitize-html'),
moment = require('moment'),
@ -19,23 +18,6 @@ var unDasherize = utils.unDasherize;
var dasherize = utils.dasherize;
var getURLTitle = utils.getURLTitle;
var transporter = nodemailer.createTransport({
service: 'Mandrill',
auth: {
user: secrets.mandrill.user,
pass: secrets.mandrill.password
}
});
function sendMailWhillyNilly(mailOptions) {
transporter.sendMail(mailOptions, function(err) {
if (err) {
console.log('err sending mail whilly nilly', err);
console.log('logging err but not carring');
}
});
}
function hotRank(timeValue, rank) {
/*
* Hotness ranking algorithm: http://amix.dk/blog/post/19588
@ -69,7 +51,6 @@ module.exports = function(app) {
var router = app.loopback.Router();
var User = app.models.User;
var findUserById = observeMethod(User, 'findById');
var findOneUser = observeMethod(User, 'findOne');
var Story = app.models.Story;
var findStory = observeMethod(Story, 'find');
@ -77,14 +58,7 @@ module.exports = function(app) {
var findStoryById = observeMethod(Story, 'findById');
var countStories = observeMethod(Story, 'count');
var Comment = app.models.Comment;
var findCommentById = observeMethod(Comment, 'findById');
router.get('/stories/hotStories', hotJSON);
router.get('/stories/comments/:id', comments);
router.post('/stories/comment/', commentSubmit);
router.post('/stories/comment/:id/comment', commentOnCommentSubmit);
router.put('/stories/comment/:id/edit', commentEdit);
router.get('/news/hot', hotJSON);
router.get('/stories/submit', submitNew);
router.get('/stories/submit/new-story', preSubmit);
router.post('/stories/preliminary', newStory);
@ -194,7 +168,6 @@ module.exports = function(app) {
description: story.description,
rank: story.upVotes.length,
upVotes: story.upVotes,
comments: story.comments,
id: story.id,
timeAgo: moment(story.timePosted).fromNow(),
image: story.image,
@ -224,7 +197,6 @@ module.exports = function(app) {
rank: 1,
upVotes: 1,
author: 1,
comments: 1,
image: 1,
storyLink: 1,
metaDescription: 1,
@ -250,7 +222,7 @@ module.exports = function(app) {
}
function upvote(req, res, next) {
var id = req.body.data.id;
const { id } = req.body;
var story$ = findStoryById(id).shareReplay();
story$.flatMap(function(story) {
@ -284,16 +256,6 @@ module.exports = function(app) {
);
}
function comments(req, res, next) {
var id = req.params.id;
findCommentById(id).subscribe(
function(comment) {
res.send(comment);
},
next
);
}
function newStory(req, res, next) {
if (!req.user) {
return next(new Error('Must be logged in'));
@ -407,7 +369,6 @@ module.exports = function(app) {
username: req.user.username,
email: req.user.email
},
comments: [],
image: data.image,
storyLink: storyLink,
metaDescription: data.storyMetaDescription,
@ -428,174 +389,4 @@ module.exports = function(app) {
next
);
}
function commentSubmit(req, res, next) {
var data = req.body.data;
if (!req.user) {
return next(new Error('Not authorized'));
}
var sanitizedBody = cleanData(data.body);
if (data.body !== sanitizedBody) {
req.flash('errors', {
msg: 'HTML is not allowed'
});
return res.send(true);
}
var comment = new Comment({
associatedPost: data.associatedPost,
originalStoryLink: data.originalStoryLink,
originalStoryAuthorEmail: data.originalStoryAuthorEmail,
body: sanitizedBody,
rank: 0,
upvotes: 0,
author: {
picture: req.user.picture,
userId: req.user.id,
username: req.user.username,
email: req.user.email
},
comments: [],
topLevel: true,
commentOn: Date.now()
});
commentSave(comment, findStoryById).subscribe(
function() {},
next,
function() {
res.send(true);
}
);
}
function commentOnCommentSubmit(req, res, next) {
var data = req.body.data;
if (!req.user) {
return next(new Error('Not authorized'));
}
var sanitizedBody = cleanData(data.body);
if (data.body !== sanitizedBody) {
req.flash('errors', {
msg: 'HTML is not allowed'
});
return res.send(true);
}
var comment = new Comment({
associatedPost: data.associatedPost,
body: sanitizedBody,
rank: 0,
upvotes: 0,
originalStoryLink: data.originalStoryLink,
originalStoryAuthorEmail: data.originalStoryAuthorEmail,
author: {
picture: req.user.picture,
userId: req.user.id,
username: req.user.username,
email: req.user.email
},
comments: [],
topLevel: false,
commentOn: Date.now()
});
commentSave(comment, findCommentById).subscribe(
function() {},
next,
function() {
res.send(true);
}
);
}
function commentEdit(req, res, next) {
findCommentById(req.params.id)
.doOnNext(function(comment) {
if (!req.user && comment.author.userId !== req.user.id) {
throw new Error('Not authorized');
}
})
.flatMap(function(comment) {
var sanitizedBody = cleanData(req.body.body);
if (req.body.body !== sanitizedBody) {
req.flash('errors', {
msg: 'HTML is not allowed'
});
}
comment.body = sanitizedBody;
comment.commentOn = Date.now();
return saveInstance(comment);
})
.subscribe(
function() {
res.send(true);
},
next
);
}
function commentSave(comment, findContextById) {
return saveInstance(comment)
.flatMap(function(comment) {
// Based on the context retrieve the parent
// object of the comment (Story/Comment)
return findContextById(comment.associatedPost);
})
.flatMap(function(associatedContext) {
if (associatedContext) {
associatedContext.comments.push(comment.id);
}
// NOTE(berks): saveInstance is safe
// it will automatically call onNext with null and onCompleted if
// argument is falsey or has no method save
return saveInstance(associatedContext);
})
.flatMap(function(associatedContext) {
// Find the author of the parent object
// if no username
var username = associatedContext && associatedContext.author ?
associatedContext.author.username :
null;
var query = { where: { username: username } };
return findOneUser(query);
})
// if no user is found we don't want to hit the doOnNext
// filter here will call onCompleted without running through the following
// steps
.filter(function(user) {
return !!user;
})
// if this is called user is guarenteed to exits
// this is a side effect, hence we use do/tap observable methods
.doOnNext(function(user) {
// If the emails of both authors differ,
// only then proceed with email notification
if (
comment.author &&
comment.author.email &&
user.email &&
(comment.author.email !== user.email)
) {
sendMailWhillyNilly({
to: user.email,
from: 'Team@freecodecamp.com',
subject: comment.author.username +
' replied to your post on Camper News',
text: [
'Just a quick heads-up: ',
comment.author.username,
' replied to you on Camper News.',
'You can keep this conversation going.',
'Just head back to the discussion here: ',
'http://freecodecamp.com/stories/',
comment.originalStoryLink,
'- the Free Code Camp Volunteer Team'
].join('\n')
});
}
});
}
};

View File

@ -1,164 +0,0 @@
.text-left
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")
script.
var sentinel = 0;
var renderComments = function renderComments(comments, containerSelector, level) {
var commentDetails;
R.forEach(function displayComments(comment) {
$.ajax({
type: 'GET',
url: '/stories/comments/' + comment,
error: function (xhr, textStatus, errorThrown) {
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');
var editButton = "";
// todo
if (commentDetails.author.username === DF105CFA89562196E702912B3818C6A5B46E80D262442FDF29976621E5AF0D23) {
if ((Date.now() - commentDetails.commentOn) < 600000){
editButton = "<a class='btn btn-no-shadow btn-primary btn-xs btn-primary-ghost edit-btn' id='" + commentDetails.id + "'>Edit</a> · ";
}
}
$(div)
.html(
'<div class="media media-news">' +
'<div class="media-left">' +
"<a href='/" + commentDetails.author.username + "'>" +
'<img class="media-object img-news" src="' + commentDetails.author.picture +'" alt="' + commentDetails.author.username + '">' +
'</a>' +
'</div>' +
'<div class="media-body comment_' + commentDetails.id + '">' +
'<div class="media-body-wrapper">' +
'<p>' + commentDetails.body + '</p>' +
'<h6>' +
'<div class="clearfix comment-a-comment negative-15">' +
"<a class='btn btn-no-shadow btn-primary btn-xs btn-primary-ghost' id='" + commentDetails.id + "'>Reply</a> · " +
editButton +
"commented " + moment(commentDetails.commentOn).fromNow() + " by " +
"<a href='/" + commentDetails.author.username + "'>@" + commentDetails.author.username + "</a>" +
'</div>' +
'</h6>' +
'</div>' +
'</div>' +
'</div>'
)
.addClass('comment-wrapper positive-10')
.appendTo($(containerSelector));
sentinel += commentDetails.comments.length;
renderComments(commentDetails.comments, '.comment_' + commentDetails.id, ++level);
},
complete: function () {
sentinel--;
if (!sentinel) {
$('.comment-a-comment').on('click', 'a', function() {
var editOrComment = 'comment';
if ($(this).hasClass("edit-btn")){
editOrComment = 'edit';
}
if (!isLoggedIn) {
window.location.href = '/signin';
return;
}
$(this).unbind('click');
$('.comment-to-comment-formgroup').empty();
$('#initial-comment-submit').addClass('hidden-element');
var div = document.createElement('div');
var commentId = $(this).attr('id');
$(div).html(
"<div class='formgroup comment-to-comment-formgroup control-label-story-submission'>" +
'<div class="col-xs-12">' +
"<div class='input-group'>" +
"<input class='form-control big-text-field field-responsive' id='comment-to-comment-textinput' type='text' maxlength='140' autofocus />" +
"<span class='input-group-btn'>" +
"<button id='submit-comment-to-" + editOrComment + "' class='btn btn-big btn-primary btn-responsive'>Send</button>" +
"</span>" +
"</div>" +
"</div>" +
"<div id='textarea-comment-feedback'></div>" +
"</div>"
)
.addClass('row')
.appendTo($(this).closest('.media-body-wrapper'));
var text_max = 140;
$('#textarea-comment-feedback').html(text_max + ' characters remaining');
$('#comment-to-comment-textinput').keyup(function (e) {
if (e.which === 13 || e === 13) {
$('#submit-comment-to-comment').click();
$('#submit-comment-to-edit').click();
}
var text_length = $('#comment-to-comment-textinput').val().length;
var text_remaining = text_max - text_length;
$('#textarea-comment-feedback').html(text_remaining + ' characters remaining');
});
var submitCommentToCommentHandler = function submitCommentToCommentHandler() {
$('#submit-comment-to-comment').unbind('click');
$.post('/stories/comment/' + commentId + '/comment',
{
data: {
associatedPost: commentId,
originalStoryLink: originalStoryLink,
originalStoryAuthorEmail: originalStoryAuthorEmail,
body: $('#comment-to-comment-textinput').val(),
}
})
.fail(function (xhr, textStatus, errorThrown) {
$('#submit-comment-to-comment').bind('click', submitCommentToCommentHandler);
})
.done(function (data, textStatus, xhr) {
window.location.reload();
});
};
// todo
var submitCommentForEditToCommentHandler = function submitCommentForEditToCommentHandler() {
$('#submit-comment-to-edit').unbind('click');
$.ajax({
type: "PUT",
url: '/stories/comment/' + commentId + '/edit',
data: {
associatedPost: commentId,
originalStoryLink: originalStoryLink,
body: $('#comment-to-comment-textinput').val()
},
dataType: "json",
success: function (msg) {
window.location.reload();
},
error: function (err){
$('#submit-comment-to-edit').bind('click', submitCommentForEditToCommentHandler);
}
});
};
$('#submit-comment-to-edit').on('click', submitCommentForEditToCommentHandler)
$('#submit-comment-to-comment').on('click', submitCommentToCommentHandler);
});
}
}
})
}, comments);
}
sentinel += comments.length;
renderComments(comments, $('#comment-list'), 0);

View File

@ -9,7 +9,7 @@
return name.trim().toLowerCase().replace(/\s/g, '-');
}
$.ajax({
url: '/stories/hotStories',
url: '/news/hot',
type: 'GET'
})
.success(
@ -18,6 +18,12 @@
var div = document.createElement('div');
var linkedName = getLinkedName(data[i].storyLink);
var rank = data[i].rank;
var alreadyUpvoted = false;
if (typeof username !== 'undefined') {
alreadyUpvoted = data[i].upVotes.some(function(vote) {
return vote.upVotedByUsername === username
});
}
$(div)
.html(
@ -38,7 +44,11 @@
" by <a href='/" + data[i].author.username + "'>@" + data[i].author.username + "</a> " +
"</div>" +
"<div class='col-xs-12'>" +
"<br><a class='btn btn-no-shadow btn-primary btn-block btn-primary-ghost' href='/stories/" + linkedName + "'>discuss</a>" +
"<br>" +
(typeof username !== 'undefined' ?
"<button id='" + data[i].id + "' class='btn btn-no-shadow btn-primary btn-block btn-primary-ghost btn-upvote'>upvote</button>" :
"<a href='/signup' class='btn btn-no-shadow btn-primary btn-block btn-primary-ghost btn-upvote'>upvote</a>") +
"<a class='btn btn-no-shadow btn-primary btn-block btn-primary-ghost' href='/news/" + linkedName + "'>more info...</a>" +
"</div>" +
"</div>" +
"<div class='hidden-xs row media-stories'>" +
@ -56,7 +66,10 @@
"</a>" +
"</div>" +
"<div class='story-byline col-xs-12 wrappable'>" +
"<a class='btn btn-no-shadow btn-primary btn-xs btn-primary-ghost' href='/stories/" + linkedName + "'>discuss</a> · " +
(typeof username !== 'undefined' ?
"<button id='" + data[i].id + "' class='btn btn-no-shadow btn-primary btn-xs btn-primary-ghost btn-upvote'>upvote</button>" :
"<a href='/signin' class='btn btn-no-shadow btn-primary btn-xs btn-primary-ghost'>upvote</a>") +
" · <a class='btn btn-no-shadow btn-primary btn-xs btn-primary-ghost' href='/news/" + linkedName + "'>more info...</a> · " +
rank + (rank > 1 ? " points" : " point") + " · posted " +
moment(data[i].timePosted).fromNow() +
" by <a href='/" + data[i].author.username + "'>@" + data[i].author.username + "</a> " +
@ -68,5 +81,13 @@
);
$(div).addClass('story-list news-box')
$(div).appendTo($('#story-list'));
$(div).find('.btn-upvote').each(function(idx, btn) {
var $btn = $(btn);
if (alreadyUpvoted) {
$btn.addClass('disabled');
$btn.text('upvoted!');
}
$btn.data('upVotes', data[i].upVotes);
});
}
});

View File

@ -3,8 +3,8 @@ block content
if (user)
script.
var isLoggedIn = true;
var B3BA669EC5C1DD70FB478221E067A7E1B686929C569F5E73561B69C8F42129B = !{JSON.stringify(user.id)};
var DF105CFA89562196E702912B3818C6A5B46E80D262442FDF29976621E5AF0D23 = !{JSON.stringify(user.username)};
var userId = !{JSON.stringify(user.id)};
var username = !{JSON.stringify(user.username)};
else
script.
var isLoggedIn = false;

View File

@ -1,55 +0,0 @@
.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.trim().toLowerCase().replace(/\s/g, '-');
}
$.ajax({
url: '/stories/recentStories',
type: 'GET'
})
.success(
function(data) {
for (var i = 0; i < data.length; i++) {
var div = document.createElement('div');
var linkedName = getLinkedName(data[i].storyLink);
var rank = data[i].rank;
$(div)
.html(
"<div class='col-xs-12 text-left'>" +
"<h2 class='col-xs-1 col-sm-1 positive-5'>" +
(i + 1) +
"</h2>" +
"<div class='col-xs-2 col-sm-1'>" +
"<a href='/" + data[i].author.username + "'>" +
"<img src='" + data[i].author.picture + "' class='img-news'/>" +
"</a>" +
"</div>" +
"<div class='col-xs-9 col-sm-10'>" +
"<div class='row'>" +
"<div class='story-headline col-xs-12'>" +
"<a href='" + data[i].link + "' target='_blank'>" +
data[i].headline +
"</a>" +
"</div>" +
"<div class='col-xs-12'>" +
"<span>" +
"<a class='btn btn-no-shadow btn-primary btn-xs btn-primary-ghost' href='/stories/" + linkedName + "'>discuss</a> · " +
rank + (rank > 1 ? " points" : " point") + " · posted " +
moment(data[i].timePosted).fromNow() +
" by <a href='/" + data[i].author.username + "'>@" + data[i].author.username + "</a> " +"</a> " +
"</span>" +
"</div>" +
"</div>" +
"</div>" +
"</div>"
);
$(div).addClass('story-list')
$(div).appendTo($('#story-list'));
}
});

View File

@ -84,7 +84,7 @@ script.
"</div>" +
"<div class='col-xs-12'>" +
"<br>" +
"<a class='btn btn-no-shadow btn-primary btn-block btn-primary-ghost' href='/news/" + linkedName + "'>discuss</a>" +
"<a class='btn btn-no-shadow btn-primary btn-block btn-primary-ghost' href='/news/" + linkedName + "'>more info...</a>" +
"</div>" +
"</div>" +
"</div>" +
@ -103,7 +103,7 @@ script.
"</a>" +
"</div>" +
"<div class='story-byline col-xs-12 wrappable'>" +
"<a class='btn btn-no-shadow btn-primary btn-xs btn-primary-ghost' href='/news/" + linkedName + "'>discuss</a> · " +
"<a class='btn btn-no-shadow btn-primary btn-xs btn-primary-ghost' href='/news/" + linkedName + "'>more info.more info...</a> · " +
rank + (rank > 1 ? " points" : " point") + " · posted " +
moment(data[i].timePosted).fromNow() +
" by <a href='/" + data[i].author.username + "'>@" + data[i].author.username +

View File

@ -2,7 +2,6 @@ script.
var storyId = !{JSON.stringify(id)};
var originalStoryLink = !{JSON.stringify(originalStoryLink)};
var originalStoryAuthorEmail = !{JSON.stringify(originalStoryAuthorEmail)};
var comments = !{JSON.stringify(comments)};
var upVotes = !{JSON.stringify(upVotes)};
var image = !{JSON.stringify(image)};
var hasUserVoted = !{JSON.stringify(hasUserVoted)};
@ -26,12 +25,6 @@ h3.row
.col-xs-12
h4= description
.negative-5
if !user
a#upvote.btn.signup-btn.btn-xs(href='/signin') Sign in to reply or upvote
| &thinsp;·&thinsp;
else
a#reply-to-main-post.btn.btn-no-shadow.btn-primary.btn-xs.btn-primary-ghost Reply
| &thinsp;·&thinsp;&thinsp;
if !hasUserVoted
a#upvote.btn.btn-no-shadow.btn-primary.btn-xs.btn-primary-ghost Upvote
| &thinsp;·&thinsp;
@ -44,34 +37,7 @@ h3.row
span &thinsp;by&thinsp;
a(href="/" + author.username) @#{author.username}
if (user !== null)
.col-xs-12#reply-area
.hidden-element#initial-comment-submit
form.form-horizontal.control-label-story-submission
.col-xs-12
.input-group
input#comment-box.big-text-field.field-responsive.form-control(type='text', placeholder='Enter your reply', autofocus)
span.input-group-btn
button#comment-button.btn.btn-big.btn-primary.btn-responsive(type='button') Send
span.spacer.pull-left#textarea_feedback
script.
if (image) {
$('#image-display').removeClass('hidden-element')
}
$('#reply-to-main-post').on('click', function() {
$('#initial-comment-submit').removeClass('hidden-element');
$(this).unbind('click');
$('.comment-to-comment-formgroup').empty();
});
var text_max = 140;
$('#textarea_feedback').html(text_max + ' characters remaining');
$('#comment-box').keyup(function (e) {
if (e.which === 13 || e === 13) {
$('#comment-button').click();
}
var text_length = $('#comment-box').val().length;
var text_remaining = text_max - text_length;
$('#textarea_feedback').html(text_remaining + ' characters remaining');
});
include comments

View File

@ -22,11 +22,6 @@
alert(type='danger')
span.ion-close-circled
| A headline is required to submit.
.form-group
.col-xs-12.col-md-1
label.control-label.control-label-story-submission(for='name') Comment
.col-xs-12.col-md-11
input#description-box.form-control(name="comment-box", placeholder="Kick off the discussion with an optional comment about this link" maxlength='140')
.form-group
.col-xs-12.col-md-offset-1
span.pull-left#textarea_feedback