Add comment voting using graphql union type

This commit is contained in:
SachaG 2017-01-29 11:17:00 +09:00
parent ae92ac3d45
commit 38de180530
8 changed files with 73 additions and 34 deletions

View file

@ -100,6 +100,9 @@ class CommentsItem extends Component{
<div className="comments-item" id={comment._id}>
<div className="comments-item-body">
<div className="comments-item-meta">
<div className="comments-item-vote">
<Components.Vote collection={Comments} document={this.props.comment} currentUser={this.props.currentUser}/>
</div>
<Components.UsersAvatar size="small" user={comment.user}/>
<Components.UsersName user={comment.user}/>
<div className="comments-item-date"><FormattedRelative value={comment.postedAt}/></div>

View file

@ -1,7 +1,7 @@
import { Components, registerComponent } from 'meteor/nova:lib';
import React, { PropTypes, Component } from 'react';
import classNames from 'classnames';
import { withCurrentUser, withMessages } from 'meteor/nova:core';
import { withMessages } from 'meteor/nova:core';
import { withVote, hasUpvoted, hasDownvoted } from 'meteor/nova:voting';
class Vote extends Component {

View file

@ -72,6 +72,16 @@ PostsCommentsThread.fragment = gql`
}
}
userId
upvoters {
_id
}
downvoters {
_id
}
upvotes # should be asked only for admins?
downvotes # should be asked only for admins?
baseScore # should be asked only for admins?
score # should be asked only for admins?
}
`;

View file

@ -67,7 +67,6 @@ PostsList.fragment = gql`
url
slug
thumbnailUrl
baseScore
postedAt
sticky
status
@ -92,6 +91,8 @@ PostsList.fragment = gql`
_id
}
upvotes # should be asked only for admins?
downvotes # should be asked only for admins?
baseScore # should be asked only for admins?
score # should be asked only for admins?
viewCount # should be asked only for admins?
clickCount # should be asked only for admins?

View file

@ -31,6 +31,11 @@
.comments-item-meta{
@include flex-center;
margin-bottom: $vmargin;
.comments-item-vote{
margin-right: 10px;
}
.users-avatar{
margin-right: 5px;
}

View file

@ -8,16 +8,30 @@ const withVote = component => {
return graphql(gql`
mutation vote($documentId: String, $voteType: String, $collectionName: String) {
vote(documentId: $documentId, voteType: $voteType, collectionName: $collectionName) {
_id
upvotes
upvoters {
... on Post {
_id
upvotes
upvoters {
_id
}
downvotes
downvoters {
_id
}
baseScore
}
downvotes
downvoters {
... on Comment {
_id
upvotes
upvoters {
_id
}
downvotes
downvoters {
_id
}
baseScore
}
baseScore
}
}
`, {

View file

@ -1,5 +1,5 @@
import { GraphQLSchema, Utils } from 'meteor/nova:core';
import { mutateItem, operateOnItem } from './vote.js';
import { mutateItem } from './vote.js';
const voteSchema = `
type Vote {
@ -7,28 +7,31 @@ const voteSchema = `
power: Float
votedAt: String
}
type VoteResult {
_id: String
upvoters: [User]
upvotes: Float
downvoters: [User]
downvotes: Float
baseScore: Float
}
union Votable = Post | Comment
`;
GraphQLSchema.addSchema(voteSchema);
/*
const resolverMap = {
Votable: {
__resolveType(obj, context, info){
if(obj.title){
return 'Post';
}
Note: although returning a VoteResult object should in theory work,
this currently messes the automatic store update on the client.
So return a Post for now.
if(obj.postId){
return 'Comment';
}
*/
return null;
},
},
};
// GraphQLSchema.addMutation('vote(documentId: String, voteType: String, collectionName: String) : VoteResult');
GraphQLSchema.addMutation('vote(documentId: String, voteType: String, collectionName: String) : Post');
GraphQLSchema.addResolvers(resolverMap);
GraphQLSchema.addMutation('vote(documentId: String, voteType: String, collectionName: String) : Votable');
const voteResolver = {
Mutation: {

View file

@ -31,28 +31,30 @@ export const operateOnItem = function (collection, originalItem, user, operation
const votePower = getVotePower(user);
const hasUpvotedItem = hasUpvoted(user, item);
const hasDownvotedItem = hasDownvoted(user, item);
const modifier = {};
const collectionName = collection._name;
const canDo = Users.canDo(user, `${collectionName}.${operation}`);
// console.log('// operateOnItem')
// console.log('isClient: ',isClient)
// console.log('collection: ',collection._name)
// console.log('operation: ',operation)
// console.log('item: ',item)
// console.log('user: ',user)
const collectionName = collection._name;
// console.log('isClient: ', isClient)
// console.log('collection: ', collectionName)
// console.log('operation: ', operation)
// console.log('item: ', item)
// console.log('user: ', user)
// console.log('hasUpvotedItem: ', hasUpvotedItem)
// console.log('hasDownvotedItem: ', hasDownvotedItem)
// console.log('canDo: ', canDo)
// make sure item and user are defined, and user can perform the operation
if (
!item ||
!user ||
!Users.canDo(user, `${collectionName}.${operation}`) ||
!canDo ||
operation === "upvote" && hasUpvotedItem ||
operation === "downvote" && hasDownvotedItem ||
operation === "cancelUpvote" && !hasUpvotedItem ||
operation === "cancelDownvote" && !hasDownvotedItem
) {
return false;
throw new Meteor.Error(`Cannot perform operation "${collectionName}.${operation}"`);
}
// ------------------------------ Sync Callbacks ------------------------------ //
@ -131,6 +133,7 @@ Call operateOnItem, update the db with the result, run callbacks.
export const mutateItem = function (collection, originalItem, user, operation) {
const newItem = operateOnItem(collection, originalItem, user, operation, false);
newItem.inactive = false;
collection.update({_id: newItem._id}, newItem, {bypassCollection2:true});
// --------------------- Server-Side Async Callbacks --------------------- //