diff --git a/packages/base-components/lib/posts/Post.jsx b/packages/base-components/lib/posts/Post.jsx
index 3ad1861fd..dc74c8755 100644
--- a/packages/base-components/lib/posts/Post.jsx
+++ b/packages/base-components/lib/posts/Post.jsx
@@ -1,17 +1,22 @@
-const Post = ({document}) => {
-
- ({ListContainer, CommentList, CommentNew, PostCategories, SocialShare, HeadTags} = Telescope.components);
+const Post = ({document, currentUser}) => {
+
+ ({ListContainer, CommentList, CommentNew, PostCategories, SocialShare, Vote, PostStats, HeadTags} = Telescope.components);
const post = document;
const htmlBody = {__html: post.htmlBody};
return (
+
+
{post.title}
{post.commentCount} comments
{moment(post.postedAt).fromNow()}
+
+
+
{post.categoriesArray ? : ""}
diff --git a/packages/base-components/lib/posts/PostDaily.jsx b/packages/base-components/lib/posts/PostDaily.jsx
new file mode 100644
index 000000000..8b94d8b9b
--- /dev/null
+++ b/packages/base-components/lib/posts/PostDaily.jsx
@@ -0,0 +1,45 @@
+import React, { PropTypes, Component } from 'react';
+
+// for a number of days "n" return dates object for the past n days
+const getLastNDates = n => {
+ return _.range(n).map(
+ i => moment().subtract(i, 'days').startOf('day').toDate()
+ );
+};
+
+class PostDaily extends Component{
+
+ constructor(props) {
+ super(props);
+ this.loadMoreDays = this.loadMoreDays.bind(this);
+ this.state = {days: props.days};
+ }
+
+ loadMoreDays(e) {
+ e.preventDefault();
+ this.setState({
+ days: this.state.days + 5
+ });
+ }
+
+ render() {
+ ({PostDay} = Telescope.components);
+ return (
+
+ {getLastNDates(this.state.days).map((date, index) =>
)}
+ Load More Days
+
+ )
+ }
+}
+
+PostDaily.propTypes = {
+ days: React.PropTypes.number
+}
+
+PostDaily.defaultProps = {
+ days: 5
+}
+
+module.exports = PostDaily;
+export default PostDaily;
diff --git a/packages/base-components/lib/posts/PostDay.jsx b/packages/base-components/lib/posts/PostDay.jsx
new file mode 100644
index 000000000..67af16cb6
--- /dev/null
+++ b/packages/base-components/lib/posts/PostDay.jsx
@@ -0,0 +1,40 @@
+import React, { PropTypes, Component } from 'react';
+
+const PostDay = ({date, number}) => {
+
+ ({PostList} = Telescope.components);
+
+ const terms = {
+ view: "top",
+ date: date,
+ after: moment(date).format("YYYY-MM-DD"),
+ before: moment(date).format("YYYY-MM-DD"),
+ enableCache: number <= 15 ? true : false // only cache first 15 days
+ };
+
+ ({selector, options} = Posts.parameters.get(terms));
+
+ return (
+
+
{moment(date).format("dddd, MMMM Do YYYY")}
+
+
+ )
+}
+
+PostDay.propTypes = {
+ date: React.PropTypes.object,
+ number: React.PropTypes.number
+}
+
+module.exports = PostDay;
+export default PostDay;
\ No newline at end of file
diff --git a/packages/base-components/lib/posts/PostStats.jsx b/packages/base-components/lib/posts/PostStats.jsx
new file mode 100644
index 000000000..d5d3a19df
--- /dev/null
+++ b/packages/base-components/lib/posts/PostStats.jsx
@@ -0,0 +1,16 @@
+const PostStats = ({post}) => {
+
+ ({Icon} = Telescope.components);
+
+ return (
+
+ {post.score ? {Math.floor(post.score*10000)/10000} Score : ""}
+ {post.upvotes} Upvotes
+ {post.clickCount} Clicks
+ {post.viewCount} Views
+
+ )
+}
+
+module.exports = PostStats;
+export default PostStats;
\ No newline at end of file
diff --git a/packages/base-components/lib/posts/Vote.jsx b/packages/base-components/lib/posts/Vote.jsx
new file mode 100644
index 000000000..24fb2a1c0
--- /dev/null
+++ b/packages/base-components/lib/posts/Vote.jsx
@@ -0,0 +1,62 @@
+import React, { PropTypes, Component } from 'react';
+
+import Core from "meteor/nova:core";
+const Messages = Core.Messages;
+
+class Vote extends Component {
+
+ constructor() {
+ super();
+ this.upvote = this.upvote.bind(this);
+ }
+
+ upvote(e) {
+ e.preventDefault();
+
+ const post = this.props.post;
+ const user = this.props.currentUser;
+
+ if(!user){
+ Messages.flash("Please log in first");
+ } else if (user.hasUpvoted(post)) {
+ Meteor.call('posts.cancelUpvote', post._id, function(){
+ Events.track("post upvote cancelled", {'_id': post._id});
+ });
+ } else {
+ Meteor.call('posts.upvote', post._id, function(){
+ Events.track("post upvoted", {'_id': post._id});
+ });
+ }
+
+ }
+
+ render() {
+
+ ({Icon} = Telescope.components);
+
+ const post = this.props.post;
+ const user = this.props.currentUser;
+
+ let actionsClass = "vote";
+ if (Users.hasUpvoted(user, post)) actionsClass += " voted upvoted";
+ if (Users.hasDownvoted(user, post)) actionsClass += " voted downvoted";
+
+ return (
+
+ )
+ }
+
+}
+
+Vote.propTypes = {
+ post: React.PropTypes.object.isRequired, // the current comment
+ currentUser: React.PropTypes.object, // the current user
+}
+
+module.exports = Vote;
+export default Vote;
\ No newline at end of file
diff --git a/packages/base-components/lib/posts/list/PostItem.jsx b/packages/base-components/lib/posts/list/PostItem.jsx
index 94c1ed16e..d40ffa68d 100644
--- a/packages/base-components/lib/posts/list/PostItem.jsx
+++ b/packages/base-components/lib/posts/list/PostItem.jsx
@@ -40,16 +40,19 @@ class PostItem extends Component {
render() {
- ({UserAvatar} = Telescope.components);
+ ({UserAvatar, Vote, PostStats} = Telescope.components);
const post = this.props.post;
return (
+
{Users.getDisplayName(post.user)}, {moment(post.postedAt).fromNow()}, {post.commentCount} comments
+
+
{this.renderCategories()}
{this.renderCommenters()}
{this.renderActions()}
diff --git a/packages/base-components/lib/posts/list/PostList.jsx b/packages/base-components/lib/posts/list/PostList.jsx
index 3e0755341..2834dc1f7 100644
--- a/packages/base-components/lib/posts/list/PostList.jsx
+++ b/packages/base-components/lib/posts/list/PostList.jsx
@@ -1,11 +1,11 @@
-const PostList = ({results, currentUser, hasMore, ready, count, totalCount, loadMore}) => {
+const PostList = ({results, currentUser, hasMore, ready, count, totalCount, loadMore, showViews = true}) => {
({PostItem, LoadMore, PostsLoading, NoPosts, NoMorePosts, PostViews} = Telescope.components);
if (!!results.length) {
return (
-
+ {showViews ?
: null}
@@ -15,7 +15,7 @@ const PostList = ({results, currentUser, hasMore, ready, count, totalCount, load
} else if (!ready) {
return (
-
+ {showViews ?
: null}
@@ -24,7 +24,7 @@ const PostList = ({results, currentUser, hasMore, ready, count, totalCount, load
} else {
return (
-
+ {showViews ?
: null}
diff --git a/packages/base-components/lib/routes.jsx b/packages/base-components/lib/routes.jsx
index 6177a3a40..912615ed8 100644
--- a/packages/base-components/lib/routes.jsx
+++ b/packages/base-components/lib/routes.jsx
@@ -24,6 +24,16 @@ FlowRouter.route('/', {
}
});
+FlowRouter.route('/daily/:days?', {
+ name: 'posts.list',
+ action(params, queryParams) {
+
+ ({AppContainer, PostDaily} = Telescope.components);
+
+ mount(AppContainer, {content:
})
+ }
+});
+
FlowRouter.route('/posts/:_id', {
name: 'posts.single',
action(params, queryParams) {
diff --git a/packages/base-styles/lib/stylesheets/main.css b/packages/base-styles/lib/stylesheets/main.css
index 5c9f10295..6b85f4025 100644
--- a/packages/base-styles/lib/stylesheets/main.css
+++ b/packages/base-styles/lib/stylesheets/main.css
@@ -97,6 +97,7 @@ code{
}
.cheatsheet h3{
margin-bottom: 10px;
+ font-weight: bold;
}
.cheatsheet code{
font-size: 13px;
@@ -106,4 +107,16 @@ code{
height: 24px;
width: 24px;
display: inline-block;
+}
+
+.sr-only{
+ display: none;
+}
+
+.upvoted .upvote{
+ opacity: 0.3;
+}
+
+.post-day h2{
+ font-weight: bold;
}
\ No newline at end of file
diff --git a/packages/nova-demo/demo-app.jsx b/packages/nova-demo/demo-app.jsx
index 904bb70c9..efc0764c1 100644
--- a/packages/nova-demo/demo-app.jsx
+++ b/packages/nova-demo/demo-app.jsx
@@ -1,5 +1,7 @@
import {mount} from 'react-mounter';
+import MoviesWrapper from './demo-components.jsx';
+
//////////////////////////////////////////////////////
// Collection & Schema //
//////////////////////////////////////////////////////
diff --git a/packages/nova-demo/demo-component.jsx b/packages/nova-demo/demo-components.jsx
similarity index 83%
rename from packages/nova-demo/demo-component.jsx
rename to packages/nova-demo/demo-components.jsx
index d196c3ee5..8bd4aac72 100644
--- a/packages/nova-demo/demo-component.jsx
+++ b/packages/nova-demo/demo-components.jsx
@@ -6,20 +6,20 @@ import Core from 'meteor/nova:core';
import SmartContainers from "meteor/utilities:react-list-container";
import FormContainers from "meteor/utilities:react-form-containers";
-FlashContainer = Core.FlashContainer;
-ModalButton = Core.ModalButton;
-NewDocContainer = FormContainers.NewDocContainer;
-EditDocContainer = FormContainers.EditDocContainer;
-ListContainer = SmartContainers.ListContainer;
+const ModalButton = Core.ModalButton;
+const NewDocContainer = FormContainers.NewDocContainer;
+const EditDocContainer = FormContainers.EditDocContainer;
+const ListContainer = SmartContainers.ListContainer;
+
+const FlashContainer = Telescope.components.FlashContainer;
+const FlashMessages = Telescope.components.FlashMessages;
//////////////////////////////////////////////////////
// MoviesWrapper //
//////////////////////////////////////////////////////
-MoviesWrapper = React.createClass({
-
+class MoviesWrapper extends Component {
render() {
-
return (
@@ -27,7 +27,7 @@ MoviesWrapper = React.createClass({
-
+
)
}
-});
+}
//////////////////////////////////////////////////////
// MoviesList //
//////////////////////////////////////////////////////
-MoviesList = React.createClass({
+class MoviesList extends Component {
renderNew() {
@@ -62,7 +62,7 @@ MoviesList = React.createClass({
)
return !!this.props.currentUser ? component : "";
- },
+ }
render() {
@@ -74,12 +74,12 @@ MoviesList = React.createClass({
)
}
-});
+};
+
//////////////////////////////////////////////////////
// Movie //
//////////////////////////////////////////////////////
-Movie = React.createClass({
-
+class Movie extends Component {
renderEdit() {
@@ -96,7 +96,7 @@ Movie = React.createClass({
{this.props.currentUser && this.props.currentUser._id === movie.userId ? component : ""}
)
- },
+ }
render() {
@@ -111,6 +111,8 @@ Movie = React.createClass({
)
}
-});
+};
-const LoadMore = props => Load More ({props.count}/{props.totalCount})
\ No newline at end of file
+const LoadMore = props => Load More ({props.count}/{props.totalCount})
+
+export default MoviesWrapper
\ No newline at end of file
diff --git a/packages/nova-demo/package.js b/packages/nova-demo/package.js
index 83b12d04e..169ecef40 100644
--- a/packages/nova-demo/package.js
+++ b/packages/nova-demo/package.js
@@ -24,7 +24,6 @@ Package.onUse(function (api) {
]);
api.addFiles([
- 'demo-component.jsx',
'demo-app.jsx'
], ['client', 'server']);
diff --git a/packages/nova-posts/lib/methods.js b/packages/nova-posts/lib/methods.js
index 2989c263f..72bc6f28b 100644
--- a/packages/nova-posts/lib/methods.js
+++ b/packages/nova-posts/lib/methods.js
@@ -213,6 +213,7 @@ Meteor.methods({
'posts.upvote': function (postId) {
check(postId, String);
+ console.log("upvote")
return Telescope.operateOnItem.call(this, Posts, postId, Meteor.user(), "upvote");
},
@@ -223,6 +224,7 @@ Meteor.methods({
'posts.cancelUpvote': function (postId) {
check(postId, String);
+ console.log("cancelUpvote")
return Telescope.operateOnItem.call(this, Posts, postId, Meteor.user(), "cancelUpvote");
},
diff --git a/packages/nova-posts/lib/server/publications.js b/packages/nova-posts/lib/server/publications.js
index 2a523c66c..73d0aff27 100644
--- a/packages/nova-posts/lib/server/publications.js
+++ b/packages/nova-posts/lib/server/publications.js
@@ -70,7 +70,8 @@ Meteor.publish('posts.list', function (terms) {
terms.currentUserId = this.userId; // add currentUserId to terms
({selector, options} = Posts.parameters.get(terms));
- Counts.publish(this, 'posts.list', Posts.find(selector, options));
+ // disabled for now because of FlowRouterSSR issue
+ // Counts.publish(this, 'posts.list', Posts.find(selector, options));
options.fields = Posts.publishedFields.list;
diff --git a/packages/_nova-voting/README.md b/packages/nova-voting/README.md
similarity index 100%
rename from packages/_nova-voting/README.md
rename to packages/nova-voting/README.md
diff --git a/packages/_nova-voting/lib/custom_fields.js b/packages/nova-voting/lib/custom_fields.js
similarity index 80%
rename from packages/_nova-voting/lib/custom_fields.js
rename to packages/nova-voting/lib/custom_fields.js
index c325a1a47..bba9df9c6 100644
--- a/packages/_nova-voting/lib/custom_fields.js
+++ b/packages/nova-voting/lib/custom_fields.js
@@ -1,3 +1,5 @@
+import PublicationUtils from 'meteor/utilities:smart-publications';
+
// ------------------------------------- Posts -------------------------------- //
Posts.addField([
@@ -67,8 +69,8 @@ Posts.addField([
},
]);
-Telescope.utils.addToFields(Posts.publishedFields.list, ["upvotes", "downvotes", "baseScore", "score"]);
-Telescope.utils.addToFields(Posts.publishedFields.single, ["upvotes", "upvoters", "downvotes", "downvoters", "baseScore", "score"]);
+PublicationUtils.addToFields(Posts.publishedFields.list, ["upvotes", "upvoters", "downvotes", "downvoters", "baseScore", "score"]);
+PublicationUtils.addToFields(Posts.publishedFields.single, ["upvotes", "upvoters", "downvotes", "downvoters", "baseScore", "score"]);
// ------------------------------------- Comments -------------------------------- //
@@ -143,5 +145,5 @@ Comments.addField([
},
]);
-Telescope.utils.addToFields(Comments.publishedFields.list, ["upvotes", "downvotes", "baseScore", "score"]);
-Telescope.utils.addToFields(Comments.publishedFields.single, ["upvotes", "upvoters", "downvotes", "downvoters", "baseScore", "score"]);
+PublicationUtils.addToFields(Comments.publishedFields.list, ["upvotes", "downvotes", "baseScore", "score"]);
+PublicationUtils.addToFields(Comments.publishedFields.single, ["upvotes", "upvoters", "downvotes", "downvoters", "baseScore", "score"]);
diff --git a/packages/_nova-voting/lib/scoring.js b/packages/nova-voting/lib/scoring.js
similarity index 89%
rename from packages/_nova-voting/lib/scoring.js
rename to packages/nova-voting/lib/scoring.js
index 1ecfb2cba..4ea2e707e 100644
--- a/packages/_nova-voting/lib/scoring.js
+++ b/packages/nova-voting/lib/scoring.js
@@ -37,10 +37,8 @@ Telescope.updateScore = function (args) {
// time decay factor
var f = 1.3;
- // use baseScore if defined, if not just use the number of votes
- // note: for transition period, also use votes if there are more votes than baseScore
- // var baseScore = Math.max(item.votes || 0, item.baseScore || 0);
- var baseScore = item.baseScore;
+ // use baseScore if defined, if not just use 0
+ var baseScore = item.baseScore || 0;
// HN algorithm
var newScore = baseScore / Math.pow(ageInHours + 2, f);
diff --git a/packages/_nova-voting/lib/server/cron.js b/packages/nova-voting/lib/server/cron.js
similarity index 94%
rename from packages/_nova-voting/lib/server/cron.js
rename to packages/nova-voting/lib/server/cron.js
index c14dc146e..7034bf49c 100644
--- a/packages/_nova-voting/lib/server/cron.js
+++ b/packages/nova-voting/lib/server/cron.js
@@ -1,9 +1,10 @@
Meteor.startup(function () {
- var scoreInterval = Settings.get("scoreUpdateInterval") || 30;
+ var scoreInterval = Telescope.settings.get("scoreUpdateInterval") || 30;
if (scoreInterval > 0) {
// active items get updated every N seconds
Meteor.setInterval(function () {
+
var updatedPosts = 0;
var updatedComments = 0;
// console.log('tick ('+scoreInterval+')');
diff --git a/packages/_nova-voting/lib/vote.js b/packages/nova-voting/lib/vote.js
similarity index 100%
rename from packages/_nova-voting/lib/vote.js
rename to packages/nova-voting/lib/vote.js
diff --git a/packages/_nova-voting/package.js b/packages/nova-voting/package.js
similarity index 82%
rename from packages/_nova-voting/package.js
rename to packages/nova-voting/package.js
index 1120fded9..8064ead71 100644
--- a/packages/_nova-voting/package.js
+++ b/packages/nova-voting/package.js
@@ -11,6 +11,11 @@ Package.onUse(function (api) {
api.use(['nova:core@0.25.7']);
+ api.use([
+ 'nova:posts@0.25.7',
+ 'nova:comments@0.25.7'
+ ], ['client', 'server']);
+
api.addFiles([
'lib/scoring.js',
'lib/vote.js',