diff --git a/packages/concordia-app/public/locales/en/translation.json b/packages/concordia-app/public/locales/en/translation.json index b77fbb1..df36e49 100644 --- a/packages/concordia-app/public/locales/en/translation.json +++ b/packages/concordia-app/public/locales/en/translation.json @@ -90,6 +90,7 @@ "topic.poll.invalid.data.header": "This topic has a poll but the data are untrusted!", "topic.poll.invalid.data.sub.header": "The poll data downloaded from the poster have been tampered with.", "topic.poll.tab.graph.title": "Results", + "topic.poll.tab.results.vote": "Vote", "topic.poll.tab.results.votes": "Votes", "topic.poll.tab.results.user.vote": "You voted: ", "topic.poll.tab.vote.no.changes": "This poll does not allow vote changes.", diff --git a/packages/concordia-app/src/components/PollView/PollGraph/PollChartBar/index.jsx b/packages/concordia-app/src/components/PollView/PollGraph/PollChartBar/index.jsx index 313a3e8..c5ca9d7 100644 --- a/packages/concordia-app/src/components/PollView/PollGraph/PollChartBar/index.jsx +++ b/packages/concordia-app/src/components/PollView/PollGraph/PollChartBar/index.jsx @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import { CHART_TYPE_BAR } from '../../../../constants/polls/PollGraph'; const PollChartBar = (props) => { - const { pollOptions, voteCounts } = props; + const { pollOptions, voteCounts, voterNames } = props; const chartOptions = useMemo(() => ({ chart: { @@ -30,9 +30,20 @@ const PollChartBar = (props) => { }, }, tooltip: { - enabled: false, + enabled: true, + x: { + show: false, + }, + y: { + formatter(value, { seriesIndex }) { + return `
${voterNames[seriesIndex].join('
')}
`; + }, + title: { + formatter: () => null, + }, + }, }, - }), [pollOptions]); + }), [pollOptions, voterNames]); const chartSeries = useMemo(() => [{ name: 'Votes', diff --git a/packages/concordia-app/src/components/PollView/PollGraph/PollChartDonut/index.jsx b/packages/concordia-app/src/components/PollView/PollGraph/PollChartDonut/index.jsx index 03acf4f..3d0644c 100644 --- a/packages/concordia-app/src/components/PollView/PollGraph/PollChartDonut/index.jsx +++ b/packages/concordia-app/src/components/PollView/PollGraph/PollChartDonut/index.jsx @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import { CHART_TYPE_DONUT } from '../../../../constants/polls/PollGraph'; const PollChartDonut = (props) => { - const { pollOptions, voteCounts } = props; + const { pollOptions, voteCounts, voterNames } = props; const chartOptions = useMemo(() => ({ chart: { @@ -30,12 +30,21 @@ const PollChartDonut = (props) => { }, labels: pollOptions, tooltip: { - enabled: false, + enabled: true, + fillSeriesColor: false, + y: { + formatter(value, { seriesIndex }) { + return `
${voterNames[seriesIndex].join('
')}
`; + }, + title: { + formatter: () => null, + }, + }, }, legend: { position: 'bottom', }, - }), [pollOptions]); + }), [pollOptions, voterNames]); return ( { - const { pollOptions, voteCounts, userVoteIndex } = props; + const { pollOptions, userVoteIndex, voteCounts, voterNames } = props; + const [totalVotes, setTotalVotes] = useState(voteCounts.reduce((accumulator, voteCount) => accumulator + voteCount, 0)); const { t } = useTranslation(); + useEffect(() => { + setTotalVotes(voteCounts.reduce((accumulator, voteCount) => accumulator + voteCount, 0)); + }, [voteCounts]); + const footer = useMemo(() => ( <> - {voteCounts.reduce((accumulator, voteCount) => accumulator + voteCount, 0)} + { totalVotes } - {t('topic.poll.tab.results.votes')} + + { totalVotes !== 1 ? t('topic.poll.tab.results.votes') : t('topic.poll.tab.results.vote') } + {userVoteIndex !== -1 && ( @@ -26,7 +33,7 @@ const PollGraph = (props) => { )} - ), [pollOptions, t, userVoteIndex, voteCounts]); + ), [pollOptions, t, totalVotes, userVoteIndex]); const panes = useMemo(() => { const chartBarPane = ( @@ -38,6 +45,7 @@ const PollGraph = (props) => { @@ -59,6 +67,7 @@ const PollGraph = (props) => { @@ -76,7 +85,7 @@ const PollGraph = (props) => { { menuItem: { key: 'chart-bar', icon: 'chart bar' }, render: () => chartBarPane }, { menuItem: { key: 'chart-donut', icon: 'chart pie' }, render: () => chartDonutPane }, ]); - }, [footer, pollOptions, voteCounts]); + }, [footer, pollOptions, voteCounts, voterNames]); return ( { const [pollOptions, setPollOptions] = useState([]); const [voteCounts, setVoteCounts] = useState([]); const [voters, setVoters] = useState([]); + const [voterNames, setVoterNames] = useState([]); const [pollHashValid, setPollHashValid] = useState(true); const [pollQuestion, setPollQuestion] = useState(''); const [chainDataLoading, setChainDataLoading] = useState(true); @@ -84,6 +85,11 @@ const PollView = (props) => { .slice(index > 0 ? cumulativeSum[index - 1] : 0, subArrayEnd))); + setVoterNames(cumulativeSum + .map((subArrayEnd, index) => pollResults.value[6] + .slice(index > 0 ? cumulativeSum[index - 1] : 0, + subArrayEnd))); + setChainDataLoading(false); } }, [getPollCallHash, getPollResults]); @@ -148,10 +154,11 @@ const PollView = (props) => { pollOptions={pollOptions} voteCounts={voteCounts} userVoteIndex={userVoteIndex} + voterNames={voterNames} /> ) : null - ), [chainDataLoading, orbitDataLoading, pollOptions, userVoteIndex, voteCounts]); + ), [chainDataLoading, orbitDataLoading, pollOptions, userVoteIndex, voteCounts, voterNames]); const panes = useMemo(() => { const pollVotePane = ( diff --git a/packages/concordia-contracts/contracts/Forum.sol b/packages/concordia-contracts/contracts/Forum.sol index dca35fc..66ccd64 100644 --- a/packages/concordia-contracts/contracts/Forum.sol +++ b/packages/concordia-contracts/contracts/Forum.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.1; contract Forum { // Error messages for require() + string public constant USER_HAS_SIGNED_UP = "User has already signed up."; string public constant USER_HAS_NOT_SIGNED_UP = "User hasn't signed up yet."; string public constant USERNAME_TAKEN = "Username is already taken."; string public constant TOPIC_DOES_NOT_EXIST = "Topic doesn't exist."; @@ -28,7 +29,7 @@ contract Forum { event UsernameUpdated(string newName, string oldName, address userAddress); function signUp(string memory username) public returns (bool) { - require(!hasUserSignedUp(msg.sender), USER_HAS_NOT_SIGNED_UP); + require(!hasUserSignedUp(msg.sender), USER_HAS_SIGNED_UP); require(!isUserNameTaken(username), USERNAME_TAKEN); users[msg.sender] = User(username, new uint[](0), new uint[](0), block.timestamp, true); usernameAddresses[username] = msg.sender; @@ -118,6 +119,16 @@ contract Forum { return userAddresses; } + function getUsernames(address[] memory userAddressesArray) public view returns (string[] memory) { + string[] memory usernamesArray = new string[](userAddressesArray.length); + + for (uint i = 0; i < userAddressesArray.length; i++) { + usernamesArray[i] = getUsername(userAddressesArray[i]); + } + + return usernamesArray; + } + //----------------------------------------POSTING---------------------------------------- struct Topic { uint topicID; diff --git a/packages/concordia-contracts/contracts/Voting.sol b/packages/concordia-contracts/contracts/Voting.sol index b11d9b1..03eb04c 100644 --- a/packages/concordia-contracts/contracts/Voting.sol +++ b/packages/concordia-contracts/contracts/Voting.sol @@ -68,26 +68,29 @@ contract Voting { ); } - function getPoll(uint topicID) public view returns (uint, string memory, bool, uint, uint[] memory, address[] memory, uint) { + function getPoll(uint topicID) public view + returns (uint, string memory, bool, uint, uint[] memory, address[] memory, string[] memory) { require(pollExists(topicID), POLL_DOES_NOT_EXIST); - uint totalVotes = getTotalVotes(topicID); uint[] memory voteCounts = getVoteCounts(topicID); - address[] memory voters = getSerializedVoters(topicID, voteCounts, totalVotes); + address[] memory voters = getSerializedVoters(topicID, voteCounts); + string[] memory voterNames = forum.getUsernames(voters); + + Poll storage poll = polls[topicID]; return ( - polls[topicID].numOptions, - polls[topicID].dataHash, - polls[topicID].enableVoteChanges, - polls[topicID].timestamp, - voteCounts, - voters, - totalVotes + poll.numOptions, + poll.dataHash, + poll.enableVoteChanges, + poll.timestamp, + voteCounts, + voters, + voterNames ); } - function getSerializedVoters(uint topicID, uint[] memory voteCounts, uint totalVotes) private view returns (address[] memory) { - + function getSerializedVoters(uint topicID, uint[] memory voteCounts) private view returns (address[] memory) { + uint totalVotes = getTotalVotes(topicID); address[] memory voters = new address[](totalVotes); uint serializationIndex = 0; @@ -99,7 +102,7 @@ contract Voting { } } - return (voters); + return voters; } function isOptionValid(uint topicID, uint option) private view returns (bool) {