Skip to content

Commit

Permalink
Fleshed out commenting feature
Browse files Browse the repository at this point in the history
  • Loading branch information
dekkerglen committed Oct 21, 2019
1 parent eb09a57 commit a3095d6
Show file tree
Hide file tree
Showing 13 changed files with 396 additions and 34 deletions.
18 changes: 16 additions & 2 deletions models/blog.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
let mongoose = require('mongoose');

// Cube schema
//this pattern lets us define comment recursively
var Comment = new mongoose.Schema();
Comment.add({
owner:String,
ownerName:String,
ownerImage:String,
content:String,
index:Number,
timePosted:Date,
comments:[Comment]
});

// Blog schema
let blogSchema = mongoose.Schema({
title: String,
body: String,
Expand All @@ -10,7 +22,9 @@ let blogSchema = mongoose.Schema({
html: String,
dev: String,
date_formatted: String,
changelist: String
changelist: String,
comments: [Comment]
});


let Blog = module.exports = mongoose.model('Blog', blogSchema)
9 changes: 7 additions & 2 deletions public/css/stylesheet.css
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,11 @@ div.ReactTags__suggestions ul li.ReactTags__activeSuggestion {
cursor: pointer;
}

div.card.shadowed {
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 6px 10px 0 rgba(0, 0, 0, 0.10);
div.card {
box-shadow: 2px 2px 6px 0 rgba(0, 0, 0, 0.06);
border-radius: 0px;
}

body.busy-cursor {
cursor: progress;
}
71 changes: 69 additions & 2 deletions routes/cube_routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ router.get('/blog/:id/:page', async function(req, res) {
if (!page) {
page = 0;
}
for (i = 0; i < blogs.length / 10; i++) {
for (var i = 0; i < blogs.length / 10; i++) {
if (page == i) {
pages.push({
url: '/cube/blog/' + cube_id + '/' + i,
Expand All @@ -602,7 +602,7 @@ router.get('/blog/:id/:page', async function(req, res) {
}
}
blog_page = [];
for (i = 0; i < 10; i++) {
for (var i = 0; i < 10; i++) {
if (blogs[i + page * 10]) {
blog_page.push(blogs[i + page * 10]);
}
Expand Down Expand Up @@ -1958,6 +1958,73 @@ router.get('/api/cardimages', function(req, res) {
});
});

function insertComment(comments, position, comment) {
if(position.length <= 0)
{
comment.index = comments.length;
comments.push(comment);
return comment;
}
else {
return insertComment(comments[position[0]].comments, position.slice(1), comment);
}
}

router.post('/api/postcomment', ensureAuth, async function(req,res) {
/*
req.param = {
id:String, //blog post id
position:[], //array of indexes to represent nested position of comment
content:String
}
req.user -> use to get user fields for comment
*/
user = await User.findById(req.user._id);
post = await Blog.findById(req.body.id);

if (!user) {
return res.status(403).send({
success: 'false',
message:'Unauthorized'
});
}

if (!post) {
return res.status(404).send({
success: 'false',
message:'Post not found'
});
}

console.log(post);
console.log(req.body);

try {
//slice limits the recursive depth
var comment = insertComment(post.comments, req.body.position.slice(0,8), {
owner:user._id,
ownerName:user.username,
ownerImage:'',
content:req.body.content,
timePosted:Date.now(),
comments:[]
});
await post.save();
console.log(post);

res.status(200).send({
success: 'true',
comment: comment
});
} catch(err) {
console.log(err);
return res.status(500).send({
success: 'false',
message:err
});
}
});

router.get('/api/imagedict', function(req, res) {
res.status(200).send({
success: 'true',
Expand Down
95 changes: 95 additions & 0 deletions src/components/AgeText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React from 'react';

class AgeText extends React.Component {
constructor(props)
{
super(props);
}

inSeconds(d1, d2) {
var t2 = d2.getTime();
var t1 = d1.getTime();

return parseInt((t2-t1)/(1000));
}

inMinutes(d1, d2) {
var t2 = d2.getTime();
var t1 = d1.getTime();

return parseInt((t2-t1)/(60*1000));
}

inHours(d1, d2) {
var t2 = d2.getTime();
var t1 = d1.getTime();

return parseInt((t2-t1)/(3600*1000));
}

inDays(d1, d2) {
var t2 = d2.getTime();
var t1 = d1.getTime();

return parseInt((t2-t1)/(24*3600*1000));
}

inWeeks(d1, d2) {
var t2 = d2.getTime();
var t1 = d1.getTime();

return parseInt((t2-t1)/(24*3600*1000*7));
}

inMonths(d1, d2) {
var d1Y = d1.getFullYear();
var d2Y = d2.getFullYear();
var d1M = d1.getMonth();
var d2M = d2.getMonth();

return (d2M+12*d2Y)-(d1M+12*d1Y);
}

inYears(d1, d2) {
return d2.getFullYear()-d1.getFullYear();
}

render() {
var date = new Date(this.props.date);
var now = new Date();
var str = '';
if(this.inYears(date, now) > 0) {
str = this.inYears(date, now) + ' year';
} else if(this.inMonths(date, now) > 0) {
str = this.inMonths(date, now) + ' month';
} else if(this.inWeeks(date, now) > 0) {
str = this.inWeeks(date, now) + ' week';
} else if(this.inDays(date, now) > 0) {
str = this.inDays(date, now) + ' day';
} else if(this.inHours(date, now) > 0) {
str = this.inHours(date, now) + ' hour';
} else if(this.inMinutes(date, now) > 0) {
str = this.inMinutes(date, now) + ' minute';
} else if(this.inSeconds(date, now) > 0) {
str = this.inSeconds(date, now) + ' second';
} else {
str = 'just now';
}
if(str != 'just now')
{
if(parseInt(str) > 1)
{
str += 's ago';
}
else
{
str += ' ago';
}
}
return (
<>{str}</>
)
}
}

export default AgeText
41 changes: 29 additions & 12 deletions src/components/BlogPost.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,57 @@
import React from 'react';
import { ListGroupItem, Collapse } from 'reactstrap';
import BlogContextMenu from './BlogContextMenu';
import CommentsSection from './CommentsSection';
import CommentEntry from './CommentEntry';

class BlogPost extends React.Component {
constructor(props) {
super(props);
super(props);

this.toggle = this.toggle.bind(this);
this.state = {collapse: false};
this.onPost = this.onPost.bind(this);

this.childElement = React.createRef();
}

toggle() {
this.setState({ collapse: !this.state.collapse });
onPost(comment)
{
comment.index = this.props.post.comments.length;
this.props.post.comments.push(comment);
this.forceUpdate();
this.childElement.current.expand();
}

render() {
var post = this.props.post;
return (
<div className="card shadowed rounded-0 mt-3">
<div className="card-header p-3">
<h5 className="card-title">{post.title}<div class="float-sm-right">
<div className="card-header pl-4 pr-0 pt-2 pb-0">
<h5 className="card-title">{post.title}<div className="float-sm-right">
{this.props.canEdit &&
<BlogContextMenu className="float-sm-right" post={post} value='...'/>
}
</div></h5>
<h6 className="card-subtitle mb-2 text-muted">{post.date_formatted}</h6>
<h6 className="card-subtitle mb-2 text-muted">{post.date_formatted}</h6>
</div>
{(post.changelist && post.html) ?
<div className="row no-gutters">
<div className="col col-12 col-l-3 col-md-3 col-sm-12" style={{'borderRight': '1px solid #DFDFDF'}}>
<div className="card-body">
<div className="card-body py-2">
<p className="card-text">
<a dangerouslySetInnerHTML={{__html: post.changelist}}></a>
</p>
</div>
</div>
<div className="col col-9">
<div className="card-body">
<div className="card-body py-2">
<div className="card-text">
<a dangerouslySetInnerHTML={{__html: post.html}}></a>
</div>
</div>
</div>
</div>
:
<div className="card-body">
<div className="card-body py-2">
{post.changelist &&
<p className="card-text">
<a dangerouslySetInnerHTML={{__html: post.changelist}}></a>
Expand All @@ -60,6 +67,16 @@ class BlogPost extends React.Component {
}
</div>
}
{post.comments.length > 0 &&
<div className="card-body px-4 pt-2 pb-0 border-top">
<CommentsSection ref={this.childElement} id={post._id} comments={post.comments} position={[]}/>
</div>
}
<div className="card-body px-4 pt-2 pb-0 border-top">
<CommentEntry id={post._id} position={[]} onPost={this.onPost}>
<h6 className="comment-button mb-2 text-muted clickable">Add Comment</h6>
</CommentEntry>
</div>
</div>
);
}
Expand Down
52 changes: 52 additions & 0 deletions src/components/Comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';

import CommentEntry from './CommentEntry';
import CommentsSection from './CommentsSection';
import AgeText from './AgeText';

class Comment extends React.Component {
constructor(props)
{
super(props);

this.onPost = this.onPost.bind(this);

this.childElement = React.createRef();
}

onPost(comment)
{
comment.index = this.props.comment.comments.length;
this.props.comment.comments.push(comment);
this.forceUpdate();
this.childElement.current.expand();
}

render() {
var comment = this.props.comment;
return (
<div className='comment'>
<div className="form-group">
{comment.ownerName ? <a href={'/user/view/'+comment.owner}><small>{comment.ownerName}</small></a> : <a><small>Anonymous</small></a>}
{comment.timePosted && <a><small> - <AgeText date={comment.timePosted}/></small></a>}
<br></br>
<a>{comment.content}</a>
<div>
<a className="comment-button mb-2 text-muted clickable">Delete</a>
{' '}
<CommentEntry id={this.props.id} position={this.props.position} onPost={this.onPost}>
<span className="comment-button mb-2 text-muted clickable">Reply</span>
</CommentEntry>
</div>
</div>
{comment.comments.length > 0 &&
<div className="pl-2 border-left">
<CommentsSection ref={this.childElement} className='pl-4' id={this.props.id} comments={comment.comments} position={this.props.position} expanded={comment.expanded ? true : false} />
</div>
}
</div>
);
}
}

export default Comment
Loading

0 comments on commit a3095d6

Please sign in to comment.