Search code examples
meteor

Upvoting and downvoting posts - Meteor


Up and downvotes are functional yet I'd like to do a check like "If the user is a downvoter or an upvoter" and do the right thing which is explained below

upvote: function(postId) {    
    check(this.userId, String);    
    check(postId, String);
    var affected = Posts.update({      
        _id: postId,       
        upvoters: {$ne: this.userId}
    },{ 
        $addToSet: {
            upvoters: this.userId
        },  
        $inc: {
            upvotes: 1
        }
    });

    if (! affected)      
        throw new Meteor.Error('invalid', "You already up-voted this post");
},

downvote: function(postId) {    
    check(this.userId, String);    
    check(postId, String);
    var affected = Posts.update({      
        _id: postId,       
        downvoters: {$ne: this.userId},
    }, {      
        $addToSet: {
            downvoters: this.userId
        },  
        $inc: {
            downvotes: 1
        }
    });

    if (! affected)      
        throw new Meteor.Error('invalid', "You already down-voted this post");     
},

With my code above, users can upvote and downvote once, but they can do both...

I wrote the code for what happens if a user is a downvoter and clicks upvote but I couldn't figure out how to check if the user is a downvoter or an upvoter.

$pull: {
        downvoters: this.userId
    },
$addToSet: {
        upvoters: this.userId
    },  
    $inc: {
        downvotes: -1
    },
    $inc: {
        upvotes: 1
});

EDIT: Even though the accepted answer works fine, I found an issue with it. When you click fast, it might increment the vote count 2-3 times. Instead of incrementing vote count, I only insert userId and simply count how many IDs there are inside the upvoters/downvoters array which gives the same result & it never inserts the same userId twice.

Inside the helpers for the count:

return this.upvoters.length

Also, inArray is a useful tool for checking if the value you have is inside an array.

if($.inArray(Meteor.userId(), this.upvoters)) //gives true if the current user's ID is inside the array

Solution

  • You will have to fetch the post and see if it contains the user's id in its downvoters array:

    var post = Posts.findOne(postId);
    if (post.downvoters && _.contains(post.downvoters, this.userId)) {
      Posts.update({      
          _id: postId
        },
        {
          $pull: {
            downvoters: this.userId
          },
          $addToSet: {
            upvoters: this.userId
          },  
          $inc: {
            downvotes: -1,
            upvotes: 1
          }
        }
      });
    }