You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

127 lines
4.4 KiB

//SPDX-License-Identifier: MIT
pragma solidity 0.8.1;
import "./Forum.sol";
contract PostVoting {
Forum public forum;
constructor(Forum addr) {
forum = Forum(addr);
}
enum Option { DEFAULT, UP, DOWN } // DEFAULT -> 0, UP -> 1, DOWN -> 2
struct PostBallot {
mapping(address => Option) votes;
mapping(Option => address[]) voters;
}
mapping(uint => PostBallot) postBallots;
event UserVotedPost(address userAddress, uint postID, Option option);
function getVote(uint postID, address voter) private view returns (Option) {
require(forum.postExists(postID), forum.POST_DOES_NOT_EXIST());
return postBallots[postID].votes[voter];
}
// Gets vote count for a specific option (Option.UP/ Option.DOWN only!)
function getVoteCount(uint postID, Option option) private view returns (uint) {
require(forum.postExists(postID), forum.POST_DOES_NOT_EXIST());
return (postBallots[postID].voters[option].length);
}
function getUpvoteCount(uint postID) private view returns (uint) {
return (getVoteCount(postID, Option.UP));
}
function getDownvoteCount(uint postID) private view returns (uint) {
return (getVoteCount(postID, Option.DOWN));
}
function getTotalVoteCount(uint postID) private view returns (int) {
int upvoteCount = int(getUpvoteCount(postID));
int downvoteCount = int(getDownvoteCount(postID));
return upvoteCount - downvoteCount;
}
function getVoteInfo(uint postID) public view returns (Option, int, uint, uint) {
require(forum.postExists(postID), forum.POST_DOES_NOT_EXIST());
return (
getVote(postID, msg.sender),
getTotalVoteCount(postID),
getUpvoteCount(postID),
getDownvoteCount(postID)
);
}
// Gets voters for a specific option (Option.UP/ Option.DOWN)
function getVoters(uint postID, Option option) private view returns (address[] memory) {
require(forum.postExists(postID), forum.POST_DOES_NOT_EXIST());
return (postBallots[postID].voters[option]);
}
function getUpvoters(uint postID) public view returns (address[] memory) {
return (getVoters(postID, Option.UP));
}
function getDownvoters(uint postID) public view returns (address[] memory) {
return (getVoters(postID, Option.DOWN));
}
function getVoterIndex(uint postID, address voter) private view returns (uint) {
require(forum.hasUserSignedUp(voter), forum.USER_HAS_NOT_SIGNED_UP());
require(forum.postExists(postID), forum.POST_DOES_NOT_EXIST());
PostBallot storage postBallot = postBallots[postID];
Option votedOption = getVote(postID, voter);
address[] storage optionVoters = postBallot.voters[votedOption];
for (uint voterIndex = 0; voterIndex < optionVoters.length; voterIndex++)
if (optionVoters[voterIndex] == voter)
return voterIndex;
revert("Couldn't find voter's index!");
}
function vote(uint postID, Option option) private {
address voter = msg.sender;
require(forum.hasUserSignedUp(voter), forum.USER_HAS_NOT_SIGNED_UP());
require(forum.postExists(postID), forum.POST_DOES_NOT_EXIST());
address postAuthor = forum.getPostAuthor(postID);
require(voter != postAuthor, "Post's author cannot vote for it.");
PostBallot storage postBallot = postBallots[postID];
Option prevOption = postBallot.votes[voter];
if (prevOption == option)
return;
// Remove previous vote if exists
if (prevOption != Option.DEFAULT) {
uint voterIndex = getVoterIndex(postID, voter);
// Swap with last voter address and delete vote
postBallot.voters[prevOption][voterIndex] = postBallot.voters[prevOption][postBallot.voters[prevOption].length - 1];
postBallot.voters[prevOption].pop();
}
// Add new vote
if (option != Option.DEFAULT)
postBallot.voters[option].push(voter);
postBallot.votes[voter] = option;
emit UserVotedPost(voter, postID, option);
}
function upvote(uint postID) public {
vote(postID, Option.UP);
}
function downvote(uint postID) public {
vote(postID, Option.DOWN);
}
function unvote(uint postID) public {
vote(postID, Option.DEFAULT);
}
}