Browse Source

feat: display names of voters in graph's tooltips

develop
Ezerous 4 years ago
parent
commit
0f1c1e3b98
  1. 1
      packages/concordia-app/public/locales/en/translation.json
  2. 17
      packages/concordia-app/src/components/PollView/PollGraph/PollChartBar/index.jsx
  3. 15
      packages/concordia-app/src/components/PollView/PollGraph/PollChartDonut/index.jsx
  4. 22
      packages/concordia-app/src/components/PollView/PollGraph/index.jsx
  5. 6
      packages/concordia-app/src/components/PollView/PollGraph/styles.css
  6. 9
      packages/concordia-app/src/components/PollView/index.jsx
  7. 13
      packages/concordia-contracts/contracts/Forum.sol
  8. 29
      packages/concordia-contracts/contracts/Voting.sol

1
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.",

17
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 `<div>${voterNames[seriesIndex].join('</div><div>')}</div>`;
},
title: {
formatter: () => null,
},
},
},
}), [pollOptions]);
}), [pollOptions, voterNames]);
const chartSeries = useMemo(() => [{
name: 'Votes',

15
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 `<div>${voterNames[seriesIndex].join('</div><div>')}</div>`;
},
title: {
formatter: () => null,
},
},
},
legend: {
position: 'bottom',
},
}), [pollOptions]);
}), [pollOptions, voterNames]);
return (
<Chart

22
packages/concordia-app/src/components/PollView/PollGraph/index.jsx

@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { Grid, Statistic, Tab } from 'semantic-ui-react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
@ -7,16 +7,23 @@ import PollChartDonut from './PollChartDonut';
import './styles.css';
const PollGraph = (props) => {
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(() => (
<>
<Statistic size="mini">
<Statistic.Value>
{voteCounts.reduce((accumulator, voteCount) => accumulator + voteCount, 0)}
{ totalVotes }
</Statistic.Value>
<Statistic.Label>{t('topic.poll.tab.results.votes')}</Statistic.Label>
<Statistic.Label>
{ totalVotes !== 1 ? t('topic.poll.tab.results.votes') : t('topic.poll.tab.results.vote') }
</Statistic.Label>
</Statistic>
{userVoteIndex !== -1
&& (
@ -26,7 +33,7 @@ const PollGraph = (props) => {
</div>
)}
</>
), [pollOptions, t, userVoteIndex, voteCounts]);
), [pollOptions, t, totalVotes, userVoteIndex]);
const panes = useMemo(() => {
const chartBarPane = (
@ -38,6 +45,7 @@ const PollGraph = (props) => {
<PollChartBar
pollOptions={pollOptions}
voteCounts={voteCounts}
voterNames={voterNames}
/>
</Grid.Column>
<Grid.Column />
@ -59,6 +67,7 @@ const PollGraph = (props) => {
<PollChartDonut
pollOptions={pollOptions}
voteCounts={voteCounts}
voterNames={voterNames}
/>
</Grid.Column>
<Grid.Column />
@ -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 (
<Tab
@ -93,6 +102,7 @@ PollGraph.defaultProps = {
PollGraph.propTypes = {
pollOptions: PropTypes.arrayOf(PropTypes.string).isRequired,
voteCounts: PropTypes.arrayOf(PropTypes.number).isRequired,
voterNames: PropTypes.arrayOf(PropTypes.array).isRequired,
userVoteIndex: PropTypes.number,
};

6
packages/concordia-app/src/components/PollView/PollGraph/styles.css

@ -2,3 +2,9 @@
box-shadow: none;
border: none;
}
.apexcharts-tooltip {
border: 1px solid #ddd !important;
background-color: white !important;
color: black !important;
}

9
packages/concordia-app/src/components/PollView/index.jsx

@ -44,6 +44,7 @@ const PollView = (props) => {
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 = (

13
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;

29
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) {

Loading…
Cancel
Save