diff --git a/packages/nova-base-components/lib/posts/PostsPage.jsx b/packages/nova-base-components/lib/posts/PostsPage.jsx
index af2c850ce..fc1888697 100644
--- a/packages/nova-base-components/lib/posts/PostsPage.jsx
+++ b/packages/nova-base-components/lib/posts/PostsPage.jsx
@@ -1,44 +1,104 @@
-import { Components, registerComponent, withDocument, withCurrentUser } from 'meteor/nova:core';
-import React from 'react';
+import { Components, registerComponent, withDocument, withCurrentUser, getActions, withMutation } from 'meteor/nova:core';
import Posts from 'meteor/nova:posts';
+import React, { Component, PropTypes } from 'react';
+import { connect } from 'react-redux';
+import { bindActionCreators } from 'redux';
-const PostsPage = (props) => {
+class PostsPage extends Component {
+
+ render() {
+ if (this.props.loading) {
+
+ return
+
+ } else {
+
+ const post = this.props.document;
- if (props.loading) {
+ const htmlBody = {__html: post.htmlBody};
- return
+ return (
+
+
+
+
- } else {
+ {post.htmlBody ?
: null}
- const post = props.document;
+
- const htmlBody = {__html: post.htmlBody};
-
- return (
-
-
-
-
-
- {post.htmlBody ?
: null}
-
-
-
-
- )
+
+ );
+
+ }
}
-};
+
+ // triggered after the component did mount on the client
+ async componentDidMount() {
+ try {
+
+ // destructure the relevant props
+ const {
+ // from the parent component, used in withDocument, GraphQL HOC
+ documentId,
+ // from connect, Redux HOC
+ setViewed,
+ postsViewed,
+ // from withMutation, GraphQL HOC
+ increasePostViewCount,
+ } = this.props;
+
+ // a post id has been found & it's has not been seen yet on this client session
+ if (documentId && !postsViewed.includes(documentId)) {
+
+ // trigger the asynchronous mutation with postId as an argument
+ await increasePostViewCount({postId: documentId});
+
+ // once the mutation is done, update the redux store
+ setViewed(documentId);
+ }
+
+ } catch(error) {
+ console.log(error); // eslint-disable-line
+ }
+ }
+}
PostsPage.displayName = "PostsPage";
PostsPage.propTypes = {
- document: React.PropTypes.object
+ documentId: PropTypes.string,
+ document: PropTypes.object,
+ postsViewed: PropTypes.array,
+ setViewed: PropTypes.func,
+ increasePostViewCount: PropTypes.func,
}
-const options = {
+const queryOptions = {
collection: Posts,
queryName: 'postsSingleQuery',
fragmentName: 'PostsPage',
};
-registerComponent('PostsPage', PostsPage, withCurrentUser, [withDocument, options]);
+const mutationOptions = {
+ name: 'increasePostViewCount',
+ args: {postId: 'String'},
+};
+
+const mapStateToProps = state => ({ postsViewed: state.postsViewed });
+const mapDispatchToProps = dispatch => bindActionCreators(getActions().postsViewed, dispatch);
+
+registerComponent(
+ // component name used by Nova
+ 'PostsPage',
+ // React component
+ PostsPage,
+ // HOC to give access to the current user
+ withCurrentUser,
+ // HOC to load the data of the document, based on queryOptions & a documentId props
+ [withDocument, queryOptions],
+ // HOC to provide a single mutation, based on mutationOptions
+ withMutation(mutationOptions),
+ // HOC to give access to the redux store & related actions
+ connect(mapStateToProps, mapDispatchToProps)
+);
diff --git a/packages/nova-posts/lib/modules.js b/packages/nova-posts/lib/modules.js
index 1858e1eac..158b584c4 100644
--- a/packages/nova-posts/lib/modules.js
+++ b/packages/nova-posts/lib/modules.js
@@ -13,5 +13,6 @@ import './emails.js';
import './permissions.js';
import './resolvers.js';
import './mutations.js';
+import './redux.js';
-export default Posts;
\ No newline at end of file
+export default Posts;
diff --git a/packages/nova-posts/lib/mutations.js b/packages/nova-posts/lib/mutations.js
index 16d8e421d..c325cbf35 100644
--- a/packages/nova-posts/lib/mutations.js
+++ b/packages/nova-posts/lib/mutations.js
@@ -1,4 +1,4 @@
-import { newMutation, editMutation, removeMutation } from 'meteor/nova:core';
+import { newMutation, editMutation, removeMutation, GraphQLSchema } from 'meteor/nova:core';
import Users from 'meteor/nova:users';
const performCheck = (mutation, user, document) => {
@@ -85,4 +85,6 @@ const mutations = {
};
-export default mutations;
\ No newline at end of file
+GraphQLSchema.addMutation('increasePostViewCount(postId: String): Float');
+
+export default mutations;
diff --git a/packages/nova-posts/lib/redux.js b/packages/nova-posts/lib/redux.js
new file mode 100644
index 000000000..ac2dec643
--- /dev/null
+++ b/packages/nova-posts/lib/redux.js
@@ -0,0 +1,23 @@
+import { addAction, addReducer } from 'meteor/nova:core';
+
+addAction({
+ postsViewed: {
+ setViewed: (postId) => ({
+ type: 'SET_VIEWED',
+ postId,
+ }),
+ },
+});
+
+addReducer({
+ postsViewed: (state = [], action) => {
+ if (action.type === 'SET_VIEWED') {
+ return [
+ ...state,
+ action.postId,
+ ];
+ }
+
+ return state;
+ },
+});
diff --git a/packages/nova-posts/lib/resolvers.js b/packages/nova-posts/lib/resolvers.js
index d2cb827b5..51ba14a24 100644
--- a/packages/nova-posts/lib/resolvers.js
+++ b/packages/nova-posts/lib/resolvers.js
@@ -6,6 +6,11 @@ const specificResolvers = {
return context.Users.findOne({ _id: post.userId }, { fields: context.getViewableFields(context.currentUser, context.Users) });
},
},
+ Mutation: {
+ increasePostViewCount(root, { postId }, context) {
+ return context.Posts.update({_id: postId}, { $inc: { viewCount: 1 }});
+ }
+ }
};
GraphQLSchema.addResolvers(specificResolvers);