poa-dapps-voting/src/components/BallotCard.jsx

308 lines
11 KiB
React
Raw Normal View History

2018-01-12 10:44:16 -08:00
import React from "react";
import moment from "moment";
2018-01-12 08:14:34 -08:00
import { observable, action, computed } from "mobx";
2018-01-11 10:49:08 -08:00
import { inject, observer } from "mobx-react";
import { toAscii } from "../helpers";
import { constants } from "../constants";
2018-01-12 10:44:16 -08:00
import swal from "sweetalert2";
2018-01-11 10:49:08 -08:00
const ACCEPT = 1;
const REJECT = 2;
@inject("commonStore", "contractsStore", "ballotStore", "routing")
@observer
export class BallotCard extends React.Component {
2018-01-12 10:44:16 -08:00
@observable startTime;
@observable endTime;
@observable timeToFinish;
@observable creator;
@observable progress;
@observable totalVoters;
@observable isFinalized;
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
@computed get votesForNumber() {
let votes = (this.totalVoters + this.progress) / 2;
return votes;
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
@computed get votesForPercents() {
2018-01-12 11:09:04 -08:00
if (this.totalVoters <= 0) {
2018-01-12 10:44:16 -08:00
return 0;
2018-01-12 11:09:04 -08:00
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
let votesPercents = Math.round(this.votesForNumber / this.totalVoters * 100);
return votesPercents;
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
@computed get votesAgainstNumber() {
let votes = (this.totalVoters - this.progress) / 2;
return votes;
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
@computed get votesAgainstPercents() {
2018-01-12 11:09:04 -08:00
if (this.totalVoters <= 0) {
2018-01-12 10:44:16 -08:00
return 0;
2018-01-12 11:09:04 -08:00
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
let votesPercents = Math.round(this.votesAgainstNumber / this.totalVoters * 100);
return votesPercents;
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
@action("Get start time of keys ballot")
getStartTime = async () => {
const { contractsStore, id, votingType } = this.props;
let startTime = await contractsStore[votingType].getStartTime(id);
2018-01-12 11:09:04 -08:00
this.startTime = moment.utc(startTime * 1000).format("DD/MM/YYYY h:mm:ss A");
2018-01-12 10:44:16 -08:00
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
@action("Get end time of keys ballot")
getEndTime = async () => {
const { contractsStore, id, votingType } = this.props;
let endTime = await contractsStore[votingType].getEndTime(id);
2018-01-12 11:09:04 -08:00
this.endTime = moment.utc(endTime * 1000).format("DD/MM/YYYY h:mm:ss A");
2018-01-12 10:44:16 -08:00
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
@action("Calculate time to finish")
calcTimeToFinish = () => {
const now = moment();
2018-01-12 11:09:04 -08:00
const finish = moment.utc(this.endTime, "DD/MM/YYYY h:mm:ss A");
2018-01-12 10:44:16 -08:00
let ms = finish.diff(now);
2018-01-12 11:09:04 -08:00
if (ms <= 0) {
2018-01-12 10:44:16 -08:00
return this.timeToFinish = moment(0, "h").format("HH") + ":" + moment(0, "m").format("mm") + ":" + moment(0, "s").format("ss");
2018-01-12 11:09:04 -08:00
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
let dur = moment.duration(ms);
this.timeToFinish = Math.floor(dur.asHours()) + moment.utc(ms).format(":mm:ss");
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
@action("Get times")
getTimes = async () => {
await this.getStartTime();
await this.getEndTime();
this.calcTimeToFinish();
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
@action("Get creator")
getCreator = async () => {
const { contractsStore, id, votingType } = this.props;
let votingState = await contractsStore[votingType].votingState(id);
this.getValidatorFullname(votingState.creator);
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
@action("Get progress")
getProgress = async () => {
const { contractsStore, id, votingType } = this.props;
let progress = await contractsStore[votingType].getProgress(id);
this.progress = Number(progress);
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
@action("Get total voters")
getTotalVoters = async () => {
const { contractsStore, id, votingType } = this.props;
let totalVoters = await contractsStore[votingType].getTotalVoters(id);
this.totalVoters = Number(totalVoters);
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
@action("Get isFinalized")
getIsFinalized = async() => {
const { contractsStore, id, votingType } = this.props;
this.isFinalized = await contractsStore[votingType].getIsFinalized(id);
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
@action("Get validator full name")
getValidatorFullname = async (_miningKey) => {
const { contractsStore } = this.props;
let validator = await contractsStore.validatorMetadata.validators(_miningKey);
let firstName = toAscii(validator.firstName);
let lastName = toAscii(validator.lastName);
2018-01-12 11:09:04 -08:00
let fullName = `${firstName} ${lastName}`;
2018-01-12 10:44:16 -08:00
this.creator = fullName ? fullName : _miningKey;
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
isValidaVote = async () => {
const { contractsStore, id, votingType } = this.props;
let isValidVote = await contractsStore[votingType].isValidVote(id, contractsStore.votingKey);
return isValidVote;
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
isActive = async () => {
const { contractsStore, id, votingType } = this.props;
let isActive = await contractsStore[votingType].isActive(id);
return isActive;
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
vote = async ({choice}) => {
const { commonStore, contractsStore, id, votingType } = this.props;
const { push } = this.props.routing;
if (!contractsStore.isValidVotingKey) {
swal("Warning!", constants.INVALID_VOTING_KEY_MSG, "warning");
return;
}
commonStore.showLoading();
let isValidVote = await this.isValidaVote();
if (!isValidVote) {
commonStore.hideLoading();
swal("Warning!", constants.INVALID_VOTE_MSG, "warning");
return;
}
contractsStore[votingType].vote(id, choice, contractsStore.votingKey)
.on("receipt", () => {
commonStore.hideLoading();
swal("Congratulations!", constants.VOTED_SUCCESS_MSG, "success").then((result) => {
push(`${commonStore.rootPath}`);
});
})
.on("error", (e) => {
commonStore.hideLoading();
swal("Error!", e.message, "error");
});
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
finalize = async (e) => {
const { commonStore, contractsStore, id, votingType } = this.props;
const { push } = this.props.routing;
if (!contractsStore.isValidVotingKey) {
swal("Warning!", constants.INVALID_VOTING_KEY_MSG, "warning");
return;
}
if (this.isFinalized) {
swal("Warning!", constants.ALREADY_FINALIZED_MSG, "warning");
return;
}
commonStore.showLoading();
let isActive = await this.isActive();
if (isActive) {
commonStore.hideLoading();
swal("Warning!", constants.INVALID_FINALIZE_MSG, "warning");
return;
}
contractsStore[votingType].finalize(id, contractsStore.votingKey)
.on("receipt", () => {
commonStore.hideLoading();
swal("Congratulations!", constants.FINALIZED_SUCCESS_MSG, "success").then((result) => {
push(`${commonStore.rootPath}`);
});
})
.on("error", (e) => {
commonStore.hideLoading();
swal("Error!", e.message, "error");
});
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
constructor(props) {
super(props);
this.isFinalized = false;
this.getTimes();
this.getCreator();
this.getTotalVoters();
this.getProgress();
this.getIsFinalized();
}
2018-01-12 08:14:34 -08:00
2018-01-12 10:44:16 -08:00
componentDidMount() {
this.interval = setInterval(() => {
2018-01-12 11:09:04 -08:00
this.calcTimeToFinish();
}, 1000);
2018-01-12 10:44:16 -08:00
}
2018-01-12 08:14:34 -08:00
2018-01-12 10:44:16 -08:00
componentWillUnmount() {
window.clearInterval(this.interval);
}
2018-01-12 08:14:34 -08:00
2018-01-12 10:44:16 -08:00
showCard = () => {
let { commonStore } = this.props;
let show = commonStore.isActiveFilter ? !this.isFinalized : true;
return show;
}
2018-01-11 10:49:08 -08:00
2018-01-12 10:44:16 -08:00
isCreatorPattern = () => {
let { commonStore } = this.props;
if (commonStore.searchTerm) {
if (commonStore.searchTerm.length > 0) {
2018-01-12 11:09:04 -08:00
const isCreatorPattern = String(this.creator).toLowerCase().includes(commonStore.searchTerm);
2018-01-12 10:44:16 -08:00
return isCreatorPattern;
}
}
return true;
2018-01-11 10:49:08 -08:00
}
2018-01-12 10:44:16 -08:00
render () {
let { contractsStore, votingType, children, isSearchPattern } = this.props;
let ballotClass = (this.showCard() && (this.isCreatorPattern() || isSearchPattern)) ? "ballots-i" : "ballots-i display-none";
2018-01-12 11:09:04 -08:00
let threshold;
2018-01-12 10:44:16 -08:00
switch(votingType) {
case "votingToChangeKeys":
2018-01-12 11:09:04 -08:00
threshold = contractsStore.keysBallotThreshold;
2018-01-12 10:44:16 -08:00
break;
case "votingToChangeMinThreshold":
2018-01-12 11:09:04 -08:00
threshold = contractsStore.minThresholdBallotThreshold;
2018-01-12 10:44:16 -08:00
break;
case "votingToChangeProxy":
2018-01-12 11:09:04 -08:00
threshold = contractsStore.proxyBallotThreshold;
2018-01-12 10:44:16 -08:00
break;
default:
2018-01-12 11:09:04 -08:00
threshold = contractsStore.keysBallotThreshold;
2018-01-12 10:44:16 -08:00
break;
}
return (
<div className={ballotClass}>
<div className="ballots-about">
<div className="ballots-about-i ballots-about-i_name">
<div className="ballots-about-td">
<p className="ballots-about-i--title">Name</p>
</div>
<div className="ballots-about-td">
<p className="ballots-i--name">{this.creator}</p>
<p className="ballots-i--created">{this.startTime}</p>
</div>
</div>
{children}
<div className="ballots-about-i ballots-about-i_time">
<div className="ballots-about-td">
<p className="ballots-about-i--title">Time</p>
</div>
<div className="ballots-about-td">
<p className="ballots-i--time">{this.timeToFinish}</p>
<p className="ballots-i--to-close">To close</p>
</div>
</div>
2018-01-11 10:49:08 -08:00
</div>
2018-01-12 10:44:16 -08:00
<div className="ballots-i-scale">
<div className="ballots-i-scale-column">
<button type="button" onClick={(e) => this.vote({choice: REJECT})} className="ballots-i--vote ballots-i--vote_no">No</button>
<div className="vote-scale--container">
<p className="vote-scale--value">No</p>
<p className="vote-scale--votes">Votes: {this.votesAgainstNumber}</p>
<p className="vote-scale--percentage">{this.votesAgainstPercents}%</p>
<div className="vote-scale">
<div className="vote-scale--fill vote-scale--fill_yes" style={{width: `${this.votesAgainstPercents}%`}}></div>
</div>
</div>
</div>
<div className="ballots-i-scale-column">
<div className="vote-scale--container">
<p className="vote-scale--value">Yes</p>
<p className="vote-scale--votes">Votes: {this.votesForNumber}</p>
<p className="vote-scale--percentage">{this.votesForPercents}%</p>
<div className="vote-scale">
<div className="vote-scale--fill vote-scale--fill_no" style={{width: `${this.votesForPercents}%`}}></div>
</div>
</div>
<button type="button" onClick={(e) => this.vote({choice: ACCEPT})} className="ballots-i--vote ballots-i--vote_yes">Yes</button>
2018-01-11 10:49:08 -08:00
</div>
</div>
2018-01-12 10:44:16 -08:00
<div className="info">
Minimum {threshold} from {contractsStore.validatorsLength} validators is required to pass the proposal
</div>
<hr />
<div className="ballots-footer">
<div className="ballots-footer-left">
<button type="button" onClick={(e) => this.finalize(e)} className="ballots-footer-finalize">Finalize ballot</button>
<p>{constants.CARD_FINALIZE_DESCRIPTION}</p>
2018-01-11 10:49:08 -08:00
</div>
2018-01-12 10:44:16 -08:00
<div type="button" className="ballots-i--vote ballots-i--vote_no">Proxy Ballot ID: {this.props.id}</div>
2018-01-11 10:49:08 -08:00
</div>
</div>
2018-01-12 10:44:16 -08:00
);
}
2018-01-11 10:49:08 -08:00
}