Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keeping track of user votes (one vote per topic i.e Reddit)

I'm working on an application which allows users to influence player rankings and trends on a live board (fantasy football).

enter image description here

As you can see, you can upvote, or downvote players (only if you are logged in and authenticated via Firebase (Google accounts)). Otherwise, if you aren't logged in, no vote registers. That is currently working fine.

My question is, what would be the easiest way to have each user only be able to vote on a player once. The current implementation allows unlimited up or down votes for any player. I would like it so I can upvote Tom Brady, but not upvote him again. From that point, I could only change it to no vote (by clicking up again) or downvote. I see there are User UID's in firebase, so I could check to see if the UserUID has voted on the specific player in the firebase DB or not before allowing the vote to go through, but what if the user just wants to change from an up vote to a down vote?

Here's the code which deals with voting --

downvotePlayer(playerId) {

this.state.user ?
  this.database.child(playerId).transaction(function (player) {
    if (player) {
      player.votes--
    }
    return player;
  })
  :
  console.log("Must log in to vote")

}

upvotePlayer(playerId) {
this.state.user ?
  this.database.child(playerId).transaction(function (player) {
    if (player) {
      console.log(player)
      player.votes++
    }
    return player;
  })
  :
  console.log("Must be logged in to vote.")

}

Here's the layout on Firebase:

enter image description here

If I left anything critical out, please let me know and I'll throw it in a gist. Thanks in advance.


Edit:

Initial DB ref is

    this.database = firebase.database().ref().child('players');

When adding an upvote to a player, I'm doing this:

    this.database.child(playerId).transaction(function (player) {

    if (player) {
      var ref = firebase.database().ref('players').child('voters');
      ref.child(uid).set(1);
    }

Which is doing almost what it needs to, except it's creating a new node all together in firebase.

enter image description here

The "voters" should be inside of the unique player above it. Is my issue obvious? I appreciate the help sir.

like image 978
9 revs, 3 users 91% Avatar asked Jan 23 '18 00:01

9 revs, 3 users 91%


2 Answers

The simplest way to model this is to store a single counter per player per "things they can vote on". For example:

"Votes": {
  "Tom Brady": {
    "uidOfVoter1": 1,
    "uidOfVoter2": -1
  }
}

The uidOfVoter values are the unique that you give each player, i.e. the UID they get from Firebase Authentication. In a structure like this, each UID can be present only once. You then ensure that each user can only write their own vote, with Firebase security rules:

{
  "rules": {
    "Votes": {
      "$thing": {
        "$uid": {
          ".write": "auth.uid === $uid",
          ".validate": "data.isNumber() && data.val() >= -1 && data.val() <= 1"
        }
      }
    }
  }
}

Now to get the total votes, either:

  • Download the votes and count them on the client.
  • Update a counter whenever a user votes, writing security rules to ensure that the vote they write and the count are matching.
  • Use a Cloud Functions to update the count whenever a vote is cast/changed.
like image 125
Frank van Puffelen Avatar answered Oct 19 '22 02:10

Frank van Puffelen


Boolean. + is true and - is false. The existence of the UID indicates a vote(r).

I can't speak to coding react specifically, but it seems that this would be the most simple (best) way to represent what you are after.

Disable/grey-out the respective vote button on the client UI once they vote either way. Even if they hack the UI, server-side (db side) it's a boolean so you can't true true it for +2.

DB Structure
thing
  votes
    <uid1>:true,
    <uid2>:true,
    <uid3>:false,

If user undoes vote, simply delete the uid from the thing they voted on.

like image 31
Ronnie Royston Avatar answered Oct 19 '22 02:10

Ronnie Royston