mirror of
https://github.com/vale981/Vulcan
synced 2025-03-10 12:36:39 -04:00
163 lines
No EOL
4.3 KiB
JavaScript
163 lines
No EOL
4.3 KiB
JavaScript
import { registerSetting, getSetting } from 'meteor/vulcan:core';
|
|
import { createError } from 'apollo-errors';
|
|
import Votes from './votes/collection.js';
|
|
import Users from 'meteor/vulcan:users';
|
|
|
|
registerSetting('voting.maxVotes', 1, 'How many times a user can vote on the same document');
|
|
|
|
/*
|
|
|
|
Define voting operations
|
|
|
|
*/
|
|
export const voteOperations = {
|
|
'upvote': {
|
|
power: 1,
|
|
// TODO: refactor voteOptimisticResponse and performVoteOperation code
|
|
// into extensible, action-specific objects
|
|
// clientOperation: () => {
|
|
|
|
// },
|
|
// serverOperation: () => {
|
|
|
|
// }
|
|
},
|
|
'downvote': {
|
|
power: -1
|
|
},
|
|
'adminUpvote': {
|
|
power: user => Users.isAdmin(user) ? 5 : 1
|
|
},
|
|
'cancelVote': {
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
Determine a user's voting power for a given operation.
|
|
If power is a function, call it on user
|
|
|
|
*/
|
|
export const getVotePower = (user, operationType) => {
|
|
const power = voteOperations[operationType].power;
|
|
return typeof power === 'function' ? power(user) : power;
|
|
};
|
|
|
|
/*
|
|
|
|
Calculate total power of all a user's votes on a document
|
|
|
|
*/
|
|
export const calculateTotalPower = userVotes => _.pluck(userVotes, 'power').reduce((a, b) => a + b, 0);
|
|
|
|
/*
|
|
|
|
Create new vote object
|
|
|
|
*/
|
|
export const createVote = ({ documentId, collectionName, operationType, user, voteId }) => ({
|
|
_id: voteId,
|
|
itemId: documentId,
|
|
collectionName,
|
|
userId: user._id,
|
|
voteType: operationType,
|
|
power: getVotePower(user, operationType),
|
|
votedAt: new Date(),
|
|
__typename: 'Vote'
|
|
});
|
|
|
|
/*
|
|
|
|
Optimistic response for votes
|
|
|
|
*/
|
|
export const voteOptimisticResponse = ({collection, document, user, operationType = 'upvote', voteId}) => {
|
|
|
|
const collectionName = collection.options.collectionName;
|
|
|
|
// make sure item and user are defined
|
|
if (!document || !user) {
|
|
throw new Error(`Cannot perform operation '${collectionName}.${operationType}'`);
|
|
}
|
|
|
|
// console.log('// voteOptimisticResponse')
|
|
// console.log('collectionName: ', collectionName)
|
|
// console.log('document:', document)
|
|
// console.log('operationType:', operationType)
|
|
|
|
// create a "lite" version of the document that only contains relevant fields
|
|
// we do not want to affect the original item directly
|
|
const newDocument = {
|
|
_id: document._id,
|
|
baseScore: document.baseScore || 0,
|
|
__typename: collection.options.typeName,
|
|
};
|
|
|
|
if (operationType === 'cancelVote') {
|
|
|
|
// subtract vote scores
|
|
newDocument.baseScore -= calculateTotalPower(document.currentUserVotes);
|
|
|
|
// clear out all votes
|
|
newDocument.currentUserVotes = [];
|
|
|
|
} else {
|
|
|
|
// create new vote and add it to currentUserVotes array
|
|
const vote = createVote({ documentId: document._id, collectionName, operationType, user, voteId });
|
|
newDocument.currentUserVotes = [...document.currentUserVotes, vote];
|
|
|
|
// increment baseScore
|
|
const power = getVotePower(user, operationType);
|
|
newDocument.baseScore += power;
|
|
|
|
}
|
|
|
|
return newDocument;
|
|
}
|
|
|
|
/*
|
|
|
|
Server-side database operation
|
|
|
|
*/
|
|
export const performVoteOperation = ({documentId, operationType, collection, voteId, currentUser}) => {
|
|
// console.log('// performVoteMutation')
|
|
// console.log('operationType: ', operationType)
|
|
// console.log('collectionName: ', collectionName)
|
|
// console.log('// document: ', collection.findOne(documentId))
|
|
|
|
const power = getVotePower(currentUser, operationType);
|
|
const userVotes = Votes.find({itemId: documentId, userId: currentUser._id}).fetch();
|
|
|
|
if (operationType === 'cancelVote') {
|
|
|
|
// if a vote has been cancelled, delete all votes and subtract their power from base score
|
|
const scoreTotal = calculateTotalPower(userVotes);
|
|
|
|
// remove vote object
|
|
Votes.remove({itemId: documentId, userId: currentUser._id});
|
|
|
|
// update document score
|
|
collection.update({_id: documentId}, {$inc: {baseScore: -scoreTotal }});
|
|
|
|
} else {
|
|
|
|
if (userVotes.length < getSetting('voting.maxVotes')) {
|
|
|
|
// create vote and insert it
|
|
const vote = createVote({ documentId, collectionName: collection.options.collectionName, operationType, user: currentUser, voteId });
|
|
delete vote.__typename;
|
|
Votes.insert(vote);
|
|
|
|
// update document score
|
|
collection.update({_id: documentId}, {$inc: {baseScore: power }});
|
|
|
|
} else {
|
|
const VoteError = createError('voting.maximum_votes_reached', {message: 'voting.maximum_votes_reached'});
|
|
throw new VoteError();
|
|
}
|
|
|
|
}
|
|
} |