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);