2018-07-13 07:32:22 -07:00
|
|
|
import React from 'react'
|
|
|
|
import moment from 'moment'
|
|
|
|
import { observable, action, computed } from 'mobx'
|
|
|
|
import { inject, observer } from 'mobx-react'
|
|
|
|
import { messages } from '../messages'
|
2018-07-18 06:50:34 -07:00
|
|
|
import { sendTransactionByVotingKey } from '../helpers'
|
2018-07-13 07:32:22 -07:00
|
|
|
import swal from 'sweetalert2'
|
|
|
|
|
|
|
|
const ACCEPT = 1
|
|
|
|
const REJECT = 2
|
2018-09-17 09:17:56 -07:00
|
|
|
const SEND = 1
|
|
|
|
const BURN = 2
|
|
|
|
const FREEZE = 3
|
2018-07-13 07:32:22 -07:00
|
|
|
const USDateTimeFormat = 'MM/DD/YYYY h:mm:ss A'
|
2018-08-02 12:14:11 -07:00
|
|
|
const maxDetailsLength = 500
|
2018-07-13 07:32:22 -07:00
|
|
|
|
|
|
|
const zeroTimeTo = '00:00'
|
|
|
|
|
|
|
|
@inject('commonStore', 'contractsStore', 'routing', 'ballotsStore')
|
2018-01-11 10:49:08 -08:00
|
|
|
@observer
|
|
|
|
export class BallotCard extends React.Component {
|
2018-09-19 00:54:31 -07:00
|
|
|
@observable cancelDeadline = 0
|
2018-07-13 07:32:22 -07:00
|
|
|
@observable startTime
|
|
|
|
@observable endTime
|
|
|
|
@observable timeTo = {}
|
|
|
|
@observable
|
|
|
|
timeToStart = {
|
|
|
|
val: 0,
|
|
|
|
displayValue: zeroTimeTo,
|
|
|
|
title: 'To start'
|
|
|
|
}
|
|
|
|
@observable
|
|
|
|
timeToFinish = {
|
|
|
|
val: 0,
|
|
|
|
displayValue: zeroTimeTo,
|
|
|
|
title: 'To close'
|
|
|
|
}
|
2018-09-19 00:54:31 -07:00
|
|
|
@observable
|
|
|
|
timeToCancel = {
|
|
|
|
val: 0,
|
|
|
|
displayValue: zeroTimeTo
|
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
@observable creatorMiningKey
|
|
|
|
@observable creator
|
|
|
|
@observable progress
|
|
|
|
@observable totalVoters
|
2018-09-17 10:33:54 -07:00
|
|
|
@observable burnVotes
|
|
|
|
@observable freezeVotes
|
|
|
|
@observable sendVotes
|
2018-07-13 07:32:22 -07:00
|
|
|
@observable isFinalized
|
2018-09-19 00:54:31 -07:00
|
|
|
@observable isCanceled = false
|
2018-07-13 07:32:22 -07:00
|
|
|
@observable canBeFinalized
|
|
|
|
@observable hasAlreadyVoted
|
|
|
|
@observable memo
|
|
|
|
|
|
|
|
@computed
|
2018-09-19 00:54:31 -07:00
|
|
|
get cancelOrFinalizeButtonDisplayName() {
|
|
|
|
if (this.isFinalized) {
|
|
|
|
return 'Finalized'
|
|
|
|
} else if (this.isCanceled) {
|
|
|
|
return 'Canceled'
|
|
|
|
} else if (this.timeToCancel.val > 0) {
|
|
|
|
return 'Cancel ballot'
|
|
|
|
} else {
|
|
|
|
return 'Finalize ballot'
|
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
2018-09-19 00:54:31 -07:00
|
|
|
get cancelOrFinalizeButtonClass() {
|
|
|
|
if (this.isFinalized) {
|
|
|
|
return 'btn btn-primary btn-finalize disabled text-capitalize'
|
|
|
|
} else if (this.isCanceled) {
|
|
|
|
return 'btn btn-primary disabled text-capitalize'
|
|
|
|
} else if (this.timeToCancel.val > 0) {
|
|
|
|
return 'btn btn-danger text-capitalize'
|
|
|
|
} else {
|
|
|
|
return 'btn btn-primary btn-finalize text-capitalize'
|
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
2018-09-19 00:54:31 -07:00
|
|
|
get cancelOrFinalizeDescription() {
|
|
|
|
if (this.isFinalized || this.isCanceled) {
|
2018-07-13 07:32:22 -07:00
|
|
|
return ''
|
2018-09-19 00:54:31 -07:00
|
|
|
} else if (this.timeToCancel.val > 0) {
|
|
|
|
return `You can cancel this ballot within ${this.timeToCancel.displayValue}`
|
|
|
|
} else {
|
|
|
|
let description = 'Finalization is available after ballot time is finished'
|
|
|
|
if (this.canBeFinalized !== null) {
|
|
|
|
description += ' or all validators are voted'
|
|
|
|
}
|
|
|
|
return description
|
2018-03-20 15:00:48 -07:00
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
|
|
|
get votesForNumber() {
|
|
|
|
let votes = (this.totalVoters + this.progress) / 2
|
|
|
|
if (isNaN(votes)) votes = 0
|
|
|
|
return votes
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
|
|
|
get votesForPercents() {
|
|
|
|
if (this.totalVoters <= 0) {
|
|
|
|
return 0
|
2018-01-12 10:44:16 -08:00
|
|
|
}
|
2018-01-11 10:49:08 -08:00
|
|
|
|
2018-07-13 07:32:22 -07:00
|
|
|
let votesPercents = Math.round((this.votesForNumber / this.totalVoters) * 100)
|
|
|
|
if (isNaN(votesPercents)) votesPercents = 0
|
|
|
|
return votesPercents
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
|
|
|
get votesAgainstNumber() {
|
|
|
|
let votes = (this.totalVoters - this.progress) / 2
|
|
|
|
if (isNaN(votes)) votes = 0
|
|
|
|
return votes
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
|
|
|
get votesAgainstPercents() {
|
|
|
|
if (this.totalVoters <= 0) {
|
|
|
|
return 0
|
2018-01-12 10:44:16 -08:00
|
|
|
}
|
2018-01-11 10:49:08 -08:00
|
|
|
|
2018-07-13 07:32:22 -07:00
|
|
|
let votesPercents = Math.round((this.votesAgainstNumber / this.totalVoters) * 100)
|
|
|
|
if (isNaN(votesPercents)) votesPercents = 0
|
|
|
|
return votesPercents
|
|
|
|
}
|
|
|
|
|
2018-09-17 09:17:56 -07:00
|
|
|
@computed
|
|
|
|
get votesBurnNumber() {
|
|
|
|
let votes = this.burnVotes
|
|
|
|
if (isNaN(votes)) votes = 0
|
|
|
|
return votes
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
|
|
|
get votesBurnPercents() {
|
|
|
|
if (this.totalVoters <= 0) {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
let votesPercents = Math.round((this.votesBurnNumber / this.totalVoters) * 100)
|
|
|
|
if (isNaN(votesPercents)) votesPercents = 0
|
|
|
|
return votesPercents
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
|
|
|
get votesFreezeNumber() {
|
|
|
|
let votes = this.freezeVotes
|
|
|
|
if (isNaN(votes)) votes = 0
|
|
|
|
return votes
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
|
|
|
get votesFreezePercents() {
|
|
|
|
if (this.totalVoters <= 0) {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
let votesPercents = Math.round((this.votesFreezeNumber / this.totalVoters) * 100)
|
|
|
|
if (isNaN(votesPercents)) votesPercents = 0
|
|
|
|
return votesPercents
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
|
|
|
get votesSendNumber() {
|
|
|
|
let votes = this.sendVotes
|
|
|
|
if (isNaN(votes)) votes = 0
|
|
|
|
return votes
|
|
|
|
}
|
|
|
|
|
|
|
|
@computed
|
|
|
|
get votesSendPercents() {
|
|
|
|
if (this.totalVoters <= 0) {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
let votesPercents = Math.round((this.votesSendNumber / this.totalVoters) * 100)
|
|
|
|
if (isNaN(votesPercents)) votesPercents = 0
|
|
|
|
return votesPercents
|
|
|
|
}
|
|
|
|
|
2018-07-13 07:32:22 -07:00
|
|
|
@action('Calculate time to start/finish')
|
|
|
|
calcTimeTo = () => {
|
|
|
|
const _now = moment()
|
2018-09-19 00:54:31 -07:00
|
|
|
const cancel = moment.utc(this.cancelDeadline, USDateTimeFormat)
|
2018-07-13 07:32:22 -07:00
|
|
|
const start = moment.utc(this.startTime, USDateTimeFormat)
|
|
|
|
const finish = moment.utc(this.endTime, USDateTimeFormat)
|
2018-09-19 00:54:31 -07:00
|
|
|
const msCancel = cancel.diff(_now)
|
|
|
|
const msStart = start.diff(_now)
|
|
|
|
const msFinish = finish.diff(_now)
|
|
|
|
|
|
|
|
if (msCancel > 0) {
|
|
|
|
this.timeToCancel.val = msCancel
|
|
|
|
this.timeToCancel.displayValue = this.formatMs(msCancel, ':mm:ss')
|
|
|
|
} else {
|
|
|
|
this.timeToCancel.val = 0
|
|
|
|
this.timeToCancel.displayValue = zeroTimeTo
|
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
|
|
|
|
if (msStart > 0) {
|
|
|
|
this.timeToStart.val = msStart + 5000
|
|
|
|
this.timeToStart.displayValue = this.formatMs(msStart, ':mm:ss')
|
|
|
|
return (this.timeTo = this.timeToStart)
|
2018-01-12 10:44:16 -08:00
|
|
|
}
|
2018-01-11 10:49:08 -08:00
|
|
|
|
2018-07-13 07:32:22 -07:00
|
|
|
if (msFinish > 0) {
|
|
|
|
this.timeToStart.val = 0
|
|
|
|
this.timeToFinish.val = msFinish
|
|
|
|
this.timeToFinish.displayValue = this.formatMs(msFinish, ':mm:ss')
|
|
|
|
return (this.timeTo = this.timeToFinish)
|
2018-01-16 16:02:07 -08:00
|
|
|
}
|
|
|
|
|
2018-07-13 07:32:22 -07:00
|
|
|
this.timeToFinish.val = 0
|
|
|
|
this.timeToFinish.displayValue = zeroTimeTo
|
|
|
|
return (this.timeTo = this.timeToFinish)
|
|
|
|
}
|
|
|
|
|
|
|
|
formatMs(ms, format) {
|
|
|
|
let dur = moment.duration(ms)
|
|
|
|
let hours = Math.floor(dur.asHours())
|
|
|
|
hours = hours < 10 ? '0' + hours : hours
|
|
|
|
let formattedMs = hours + moment.utc(ms).format(':mm:ss')
|
|
|
|
return formattedMs
|
|
|
|
}
|
|
|
|
|
|
|
|
@action('validator has already voted')
|
|
|
|
getHasAlreadyVoted = async () => {
|
|
|
|
const { contractsStore, id, votingType } = this.props
|
|
|
|
let _hasAlreadyVoted = false
|
|
|
|
try {
|
|
|
|
_hasAlreadyVoted = await this.getContract(contractsStore, votingType).hasAlreadyVoted(
|
|
|
|
id,
|
|
|
|
contractsStore.votingKey
|
|
|
|
)
|
|
|
|
} catch (e) {
|
|
|
|
console.log(e.message)
|
2018-01-12 10:44:16 -08:00
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
this.hasAlreadyVoted = _hasAlreadyVoted
|
|
|
|
}
|
|
|
|
|
|
|
|
isValidVote = async () => {
|
|
|
|
const { contractsStore, id, votingType } = this.props
|
|
|
|
let _isValidVote
|
|
|
|
try {
|
|
|
|
_isValidVote = await this.getContract(contractsStore, votingType).isValidVote(id, contractsStore.votingKey)
|
|
|
|
} catch (e) {
|
|
|
|
console.log(e.message)
|
2018-03-22 06:33:01 -07:00
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
return _isValidVote
|
|
|
|
}
|
|
|
|
|
|
|
|
isActive = async () => {
|
|
|
|
const { contractsStore, id, votingType } = this.props
|
|
|
|
let _isActive = await this.repeatGetProperty(contractsStore, votingType, id, 'isActive', 0)
|
|
|
|
return _isActive
|
|
|
|
}
|
|
|
|
|
|
|
|
canBeFinalizedNow = async () => {
|
|
|
|
const { contractsStore, id, votingType } = this.props
|
|
|
|
let _canBeFinalizedNow = await this.repeatGetProperty(contractsStore, votingType, id, 'canBeFinalizedNow', 0)
|
|
|
|
this.canBeFinalized = _canBeFinalizedNow
|
|
|
|
}
|
|
|
|
|
|
|
|
vote = async ({ choice }) => {
|
|
|
|
if (this.timeToStart.val > 0) {
|
|
|
|
swal('Warning!', messages.ballotIsNotActiveMsg(this.timeTo.displayValue), 'warning')
|
|
|
|
return
|
2018-01-12 10:44:16 -08:00
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
const { commonStore, contractsStore, id, votingType, ballotsStore, pos } = this.props
|
|
|
|
const { push } = this.props.routing
|
|
|
|
if (!contractsStore.isValidVotingKey) {
|
|
|
|
swal('Warning!', messages.invalidVotingKeyMsg(contractsStore.votingKey), 'warning')
|
|
|
|
return
|
2018-01-12 10:44:16 -08:00
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
commonStore.showLoading()
|
|
|
|
let isValidVote = await this.isValidVote()
|
|
|
|
if (!isValidVote) {
|
|
|
|
commonStore.hideLoading()
|
|
|
|
swal('Warning!', messages.INVALID_VOTE_MSG, 'warning')
|
|
|
|
return
|
2018-06-08 06:32:07 -07:00
|
|
|
}
|
|
|
|
|
2018-07-13 07:32:22 -07:00
|
|
|
const contract = this.getContract(contractsStore, votingType)
|
|
|
|
|
2018-07-18 06:50:34 -07:00
|
|
|
sendTransactionByVotingKey(
|
|
|
|
this.props,
|
|
|
|
contract.address,
|
|
|
|
contract.vote(id, choice),
|
|
|
|
async tx => {
|
|
|
|
const ballotInfo = await contract.getBallotInfo(id, contractsStore.votingKey)
|
|
|
|
|
2018-09-17 09:17:56 -07:00
|
|
|
if (ballotInfo.hasOwnProperty('totalVoters')) {
|
|
|
|
this.totalVoters = Number(ballotInfo.totalVoters)
|
|
|
|
} else {
|
|
|
|
this.burnVotes = ballotInfo.burnVotes
|
|
|
|
this.freezeVotes = ballotInfo.freezeVotes
|
|
|
|
this.sendVotes = ballotInfo.sendVotes
|
|
|
|
this.totalVoters =
|
|
|
|
Number(ballotInfo.burnVotes) + Number(ballotInfo.freezeVotes) + Number(ballotInfo.sendVotes)
|
|
|
|
}
|
|
|
|
if (ballotInfo.hasOwnProperty('progress')) {
|
|
|
|
this.progress = Number(ballotInfo.progress)
|
|
|
|
}
|
2018-07-18 06:50:34 -07:00
|
|
|
this.isFinalized = Boolean(ballotInfo.isFinalized)
|
2018-09-19 00:54:31 -07:00
|
|
|
if (ballotInfo.hasOwnProperty('isCanceled')) {
|
|
|
|
this.isCanceled = Boolean(ballotInfo.isCanceled)
|
|
|
|
}
|
2018-07-18 06:50:34 -07:00
|
|
|
if (ballotInfo.hasOwnProperty('canBeFinalizedNow')) {
|
|
|
|
this.canBeFinalized = Boolean(ballotInfo.canBeFinalizedNow)
|
|
|
|
} else {
|
|
|
|
await this.canBeFinalizedNow()
|
|
|
|
}
|
|
|
|
this.hasAlreadyVoted = true
|
|
|
|
|
2018-09-17 09:17:56 -07:00
|
|
|
if (ballotInfo.hasOwnProperty('totalVoters')) {
|
|
|
|
ballotsStore.ballotCards[pos].props.votingState.totalVoters = this.totalVoters
|
|
|
|
} else {
|
|
|
|
ballotsStore.ballotCards[pos].props.votingState.burnVotes = this.burnVotes
|
|
|
|
ballotsStore.ballotCards[pos].props.votingState.freezeVotes = this.freezeVotes
|
|
|
|
ballotsStore.ballotCards[pos].props.votingState.sendVotes = this.sendVotes
|
|
|
|
}
|
|
|
|
if (ballotInfo.hasOwnProperty('progress')) {
|
|
|
|
ballotsStore.ballotCards[pos].props.votingState.progress = this.progress
|
|
|
|
}
|
2018-07-18 06:50:34 -07:00
|
|
|
ballotsStore.ballotCards[pos].props.votingState.isFinalized = this.isFinalized
|
2018-09-19 00:54:31 -07:00
|
|
|
ballotsStore.ballotCards[pos].props.votingState.isCanceled = this.isCanceled
|
2018-07-18 06:50:34 -07:00
|
|
|
ballotsStore.ballotCards[pos].props.votingState.canBeFinalized = this.canBeFinalized
|
|
|
|
ballotsStore.ballotCards[pos].props.votingState.hasAlreadyVoted = this.hasAlreadyVoted
|
|
|
|
|
|
|
|
swal('Congratulations!', messages.VOTED_SUCCESS_MSG, 'success').then(result => {
|
|
|
|
push(`${commonStore.rootPath}`)
|
|
|
|
})
|
|
|
|
},
|
|
|
|
messages.VOTE_FAILED_TX
|
|
|
|
)
|
2018-07-13 07:32:22 -07:00
|
|
|
}
|
2018-01-16 16:05:12 -08:00
|
|
|
|
2018-09-19 00:54:31 -07:00
|
|
|
cancelOrFinalize = e => {
|
|
|
|
if (this.isFinalized || this.isCanceled) {
|
2018-07-13 07:32:22 -07:00
|
|
|
return
|
2018-01-12 10:44:16 -08:00
|
|
|
}
|
2018-09-19 00:54:31 -07:00
|
|
|
if (this.timeToCancel.val > 0) {
|
|
|
|
this.cancel(e)
|
|
|
|
} else {
|
|
|
|
this.finalize(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cancel = async e => {
|
|
|
|
console.log('cancel function called')
|
|
|
|
const { votingState, contractsStore, commonStore, ballotsStore, votingType, id, pos } = this.props
|
|
|
|
const { push } = this.props.routing
|
|
|
|
const contract = this.getContract(contractsStore, votingType)
|
|
|
|
let canCancel = true
|
2018-01-11 10:49:08 -08:00
|
|
|
|
2018-09-19 00:54:31 -07:00
|
|
|
if (!this.timeToCancel.val) {
|
|
|
|
canCancel = false
|
|
|
|
}
|
2018-09-19 03:26:04 -07:00
|
|
|
if (votingState.creatorMiningKey.toLowerCase() !== contractsStore.miningKey.toLowerCase()) {
|
2018-09-19 00:54:31 -07:00
|
|
|
canCancel = false
|
|
|
|
}
|
|
|
|
commonStore.showLoading()
|
|
|
|
if (!votingState.creationTime) {
|
|
|
|
canCancel = false
|
|
|
|
} else {
|
|
|
|
const currentTime = Number(await contract.getTime())
|
|
|
|
if (currentTime - votingState.creationTime > contractsStore.ballotCancelingThreshold) {
|
|
|
|
canCancel = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!canCancel) {
|
|
|
|
commonStore.hideLoading()
|
|
|
|
swal('Warning!', messages.INVALID_CANCEL_MSG, 'warning')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
sendTransactionByVotingKey(
|
|
|
|
this.props,
|
|
|
|
contract.address,
|
|
|
|
contract.cancelBallot(id),
|
|
|
|
async tx => {
|
|
|
|
this.isFinalized = false
|
|
|
|
this.isCanceled = true
|
|
|
|
ballotsStore.ballotCards[pos].props.votingState.isFinalized = this.isFinalized
|
|
|
|
ballotsStore.ballotCards[pos].props.votingState.isCanceled = this.isCanceled
|
|
|
|
if (this.canBeFinalized !== null) {
|
|
|
|
this.canBeFinalized = false
|
|
|
|
ballotsStore.ballotCards[pos].props.votingState.canBeFinalized = this.canBeFinalized
|
|
|
|
}
|
|
|
|
swal('Congratulations!', messages.CANCELED_SUCCESS_MSG, 'success').then(result => {
|
|
|
|
push(`${commonStore.rootPath}`)
|
|
|
|
})
|
|
|
|
},
|
|
|
|
messages.CANCEL_BALLOT_FAILED_TX
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
finalize = async e => {
|
|
|
|
console.log('finalize function called')
|
2018-07-13 07:32:22 -07:00
|
|
|
if (this.timeToStart.val > 0) {
|
|
|
|
swal('Warning!', messages.ballotIsNotActiveMsg(this.timeTo.displayValue), 'warning')
|
|
|
|
return
|
2018-03-21 07:53:27 -07:00
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
const { commonStore, contractsStore, id, votingType, ballotsStore, pos } = this.props
|
|
|
|
const { push } = this.props.routing
|
|
|
|
if (!contractsStore.isValidVotingKey) {
|
|
|
|
swal('Warning!', messages.invalidVotingKeyMsg(contractsStore.votingKey), 'warning')
|
|
|
|
return
|
2018-01-12 12:00:26 -08:00
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
if (this.isFinalized) {
|
|
|
|
swal('Warning!', messages.ALREADY_FINALIZED_MSG, 'warning')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
commonStore.showLoading()
|
|
|
|
await this.canBeFinalizedNow()
|
|
|
|
let _canBeFinalized = this.canBeFinalized
|
|
|
|
if (_canBeFinalized === null) {
|
|
|
|
console.log('canBeFinalizedNow is not existed')
|
|
|
|
_canBeFinalized = !(await this.isActive())
|
|
|
|
}
|
|
|
|
if (!_canBeFinalized) {
|
|
|
|
commonStore.hideLoading()
|
|
|
|
swal('Warning!', messages.INVALID_FINALIZE_MSG, 'warning')
|
|
|
|
return
|
2018-01-12 11:51:32 -08:00
|
|
|
}
|
|
|
|
|
2018-07-13 07:32:22 -07:00
|
|
|
const contract = this.getContract(contractsStore, votingType)
|
|
|
|
|
2018-07-18 06:50:34 -07:00
|
|
|
sendTransactionByVotingKey(
|
|
|
|
this.props,
|
|
|
|
contract.address,
|
|
|
|
contract.finalize(id),
|
|
|
|
async tx => {
|
2018-08-04 03:41:34 -07:00
|
|
|
const events = await contract.instance.getPastEvents('BallotFinalized', {
|
|
|
|
fromBlock: tx.blockNumber,
|
|
|
|
toBlock: tx.blockNumber
|
2018-07-18 06:50:34 -07:00
|
|
|
})
|
2018-08-04 03:41:34 -07:00
|
|
|
if (events.length > 0) {
|
|
|
|
this.isFinalized = true
|
2018-09-19 00:54:31 -07:00
|
|
|
this.isCanceled = false
|
2018-08-04 03:41:34 -07:00
|
|
|
ballotsStore.ballotCards[pos].props.votingState.isFinalized = this.isFinalized
|
2018-09-19 00:54:31 -07:00
|
|
|
ballotsStore.ballotCards[pos].props.votingState.isCanceled = this.isCanceled
|
2018-08-04 03:41:34 -07:00
|
|
|
if (this.canBeFinalized !== null) {
|
|
|
|
this.canBeFinalized = false
|
|
|
|
ballotsStore.ballotCards[pos].props.votingState.canBeFinalized = this.canBeFinalized
|
|
|
|
}
|
|
|
|
swal('Congratulations!', messages.FINALIZED_SUCCESS_MSG, 'success').then(result => {
|
|
|
|
push(`${commonStore.rootPath}`)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
swal('Warning!', messages.INVALID_FINALIZE_MSG, 'warning')
|
|
|
|
}
|
2018-07-18 06:50:34 -07:00
|
|
|
},
|
|
|
|
messages.FINALIZE_FAILED_TX
|
|
|
|
)
|
2018-07-13 07:32:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
repeatGetProperty = async (contractsStore, contractType, id, methodID, tryID) => {
|
|
|
|
try {
|
|
|
|
let val = await this.getContract(contractsStore, contractType)[methodID](id)
|
|
|
|
if (tryID > 0) {
|
|
|
|
console.log(`success from Try ${tryID + 1}`)
|
|
|
|
}
|
|
|
|
return val
|
|
|
|
} catch (e) {
|
|
|
|
if (tryID < 10) {
|
|
|
|
console.log(`trying to repeat get value again... Try ${tryID + 1}`)
|
|
|
|
tryID++
|
|
|
|
await setTimeout(async () => {
|
|
|
|
this.repeatGetProperty(contractsStore, contractType, id, methodID, tryID)
|
|
|
|
}, 1000)
|
|
|
|
} else {
|
|
|
|
return null
|
|
|
|
}
|
2018-01-12 10:44:16 -08:00
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
getContract(contractsStore, contractType) {
|
|
|
|
switch (contractType) {
|
|
|
|
case 'votingToChangeKeys':
|
|
|
|
return contractsStore.votingToChangeKeys
|
|
|
|
case 'votingToChangeMinThreshold':
|
|
|
|
return contractsStore.votingToChangeMinThreshold
|
|
|
|
case 'votingToChangeProxy':
|
|
|
|
return contractsStore.votingToChangeProxy
|
2018-09-17 09:17:56 -07:00
|
|
|
case 'votingToManageEmissionFunds':
|
|
|
|
return contractsStore.votingToManageEmissionFunds
|
2018-07-13 07:32:22 -07:00
|
|
|
case 'validatorMetadata':
|
|
|
|
return contractsStore.validatorMetadata
|
|
|
|
default:
|
|
|
|
return contractsStore.votingToChangeKeys
|
2018-01-12 10:44:16 -08:00
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
getThreshold(contractsStore, votingType) {
|
|
|
|
switch (votingType) {
|
|
|
|
case 'votingToChangeKeys':
|
|
|
|
return contractsStore.keysBallotThreshold
|
|
|
|
case 'votingToChangeMinThreshold':
|
|
|
|
return contractsStore.minThresholdBallotThreshold
|
|
|
|
case 'votingToChangeProxy':
|
|
|
|
return contractsStore.proxyBallotThreshold
|
2018-09-17 09:17:56 -07:00
|
|
|
case 'votingToManageEmissionFunds':
|
|
|
|
return contractsStore.emissionFundsBallotThreshold
|
2018-07-13 07:32:22 -07:00
|
|
|
default:
|
|
|
|
return contractsStore.keysBallotThreshold
|
2018-01-12 10:44:16 -08:00
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props)
|
2018-09-19 00:54:31 -07:00
|
|
|
const { votingState, contractsStore } = this.props
|
2018-07-13 07:32:22 -07:00
|
|
|
// getTimes
|
2018-09-19 00:54:31 -07:00
|
|
|
if (
|
|
|
|
votingState.hasOwnProperty('creationTime') &&
|
|
|
|
contractsStore.ballotCancelingThreshold > 0 &&
|
2018-09-19 03:26:04 -07:00
|
|
|
votingState.creatorMiningKey === contractsStore.miningKey
|
2018-09-19 00:54:31 -07:00
|
|
|
) {
|
|
|
|
this.cancelDeadline = moment
|
|
|
|
.utc((votingState.creationTime + contractsStore.ballotCancelingThreshold) * 1000)
|
|
|
|
.format(USDateTimeFormat)
|
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
this.startTime = moment.utc(votingState.startTime * 1000).format(USDateTimeFormat)
|
|
|
|
this.endTime = moment.utc(votingState.endTime * 1000).format(USDateTimeFormat)
|
|
|
|
this.calcTimeTo()
|
|
|
|
// getCreator
|
|
|
|
this.creator = votingState.creator
|
|
|
|
this.creatorMiningKey = votingState.creatorMiningKey
|
|
|
|
// getTotalVoters
|
2018-09-17 09:17:56 -07:00
|
|
|
if (votingState.hasOwnProperty('totalVoters')) {
|
|
|
|
this.totalVoters = Number(votingState.totalVoters)
|
|
|
|
} else {
|
|
|
|
this.burnVotes = Number(votingState.burnVotes)
|
|
|
|
this.freezeVotes = Number(votingState.freezeVotes)
|
|
|
|
this.sendVotes = Number(votingState.sendVotes)
|
|
|
|
this.totalVoters = this.burnVotes + this.freezeVotes + this.sendVotes
|
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
// getProgress
|
2018-09-17 09:17:56 -07:00
|
|
|
if (votingState.hasOwnProperty('progress')) {
|
|
|
|
this.progress = Number(votingState.progress)
|
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
// getIsFinalized
|
|
|
|
this.isFinalized = votingState.isFinalized
|
2018-09-19 00:54:31 -07:00
|
|
|
// getIsCanceled
|
|
|
|
if (votingState.hasOwnProperty('isCanceled')) {
|
|
|
|
this.isCanceled = votingState.isCanceled
|
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
// canBeFinalizedNow
|
2018-09-17 10:33:54 -07:00
|
|
|
if (votingState.hasOwnProperty('canBeFinalizedNow')) {
|
|
|
|
this.canBeFinalized = votingState.canBeFinalizedNow
|
|
|
|
} else {
|
|
|
|
this.canBeFinalizedNow()
|
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
// getMemo
|
|
|
|
this.memo = votingState.memo
|
|
|
|
// hasAlreadyVoted
|
2018-08-29 02:09:35 -07:00
|
|
|
if (votingState.hasOwnProperty('alreadyVoted')) {
|
|
|
|
this.hasAlreadyVoted = votingState.alreadyVoted
|
2018-07-13 07:32:22 -07:00
|
|
|
} else {
|
|
|
|
this.getHasAlreadyVoted()
|
2018-01-12 10:44:16 -08:00
|
|
|
}
|
2018-08-02 12:14:11 -07:00
|
|
|
this.state = {
|
|
|
|
detailsCollapsed: this.memo.length > maxDetailsLength
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
toggleDetails = () => {
|
|
|
|
this.setState(prevState => ({ detailsCollapsed: !prevState.detailsCollapsed }))
|
2018-07-13 07:32:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
this.interval = setInterval(() => {
|
|
|
|
this.calcTimeTo()
|
|
|
|
}, 1000)
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
window.clearInterval(this.interval)
|
|
|
|
}
|
|
|
|
|
|
|
|
showCard = () => {
|
|
|
|
let { commonStore } = this.props
|
|
|
|
let checkToFinalizeFilter = commonStore.isToFinalizeFilter
|
2018-09-19 00:54:31 -07:00
|
|
|
? !this.isFinalized &&
|
|
|
|
!this.isCanceled &&
|
|
|
|
(this.timeToFinish.val === 0 || this.canBeFinalized) &&
|
|
|
|
this.timeToStart.val === 0
|
2018-07-13 07:32:22 -07:00
|
|
|
: true
|
2018-09-19 00:54:31 -07:00
|
|
|
let show = commonStore.isActiveFilter ? !this.isFinalized && !this.isCanceled : checkToFinalizeFilter
|
2018-07-13 07:32:22 -07:00
|
|
|
return show
|
|
|
|
}
|
|
|
|
|
|
|
|
typeName(type) {
|
|
|
|
switch (type) {
|
|
|
|
case 'votingToChangeMinThreshold':
|
|
|
|
return 'Consensus'
|
|
|
|
case 'votingToChangeKeys':
|
|
|
|
return 'Keys'
|
|
|
|
case 'votingToChangeProxy':
|
|
|
|
return 'Proxy'
|
2018-09-17 09:17:56 -07:00
|
|
|
case 'votingToManageEmissionFunds':
|
|
|
|
return 'EmissionFunds'
|
2018-07-13 07:32:22 -07:00
|
|
|
default:
|
|
|
|
return ''
|
2018-01-19 22:47:00 -08:00
|
|
|
}
|
2018-07-13 07:32:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
let { contractsStore, votingType, children } = this.props
|
|
|
|
let ballotClass = this.showCard()
|
|
|
|
? this.isFinalized
|
|
|
|
? 'ballots-i'
|
|
|
|
: 'ballots-i ballots-i-not-finalized'
|
|
|
|
: 'ballots-i display-none'
|
2018-07-17 18:27:05 -07:00
|
|
|
let voteScaleClass = 'vote-scale'
|
2018-07-13 07:32:22 -07:00
|
|
|
let hasAlreadyVotedLabel = (
|
|
|
|
<div className="ballots-i--vote ballots-i--vote-label ballots-i--vote-label-right ballots-i--vote_no">
|
|
|
|
You already voted
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
let showHasAlreadyVotedLabel = this.hasAlreadyVoted ? hasAlreadyVotedLabel : ''
|
|
|
|
const threshold = this.getThreshold(contractsStore, votingType)
|
2018-08-02 12:14:11 -07:00
|
|
|
let toggleShowMore =
|
|
|
|
this.memo.length > maxDetailsLength ? (
|
|
|
|
<span className="toggle-show more" onClick={this.toggleDetails}>
|
|
|
|
{this.state.detailsCollapsed ? 'More...' : 'Less'}
|
|
|
|
</span>
|
|
|
|
) : (
|
|
|
|
''
|
|
|
|
)
|
2018-09-17 09:17:56 -07:00
|
|
|
let votingScale
|
|
|
|
if (votingType === 'votingToManageEmissionFunds') {
|
|
|
|
votingScale = (
|
|
|
|
<div className="ballots-i-scale">
|
2018-08-13 12:24:15 -07:00
|
|
|
<div className="ballots-i-scale-column ballots-i-scale-column-3">
|
|
|
|
<button
|
|
|
|
type="button"
|
2018-09-17 09:17:56 -07:00
|
|
|
onClick={e => this.vote({ choice: BURN })}
|
|
|
|
className="btn btn-danger ballots-i--vote_btn xl m-r-20"
|
2018-08-13 12:24:15 -07:00
|
|
|
>
|
2018-09-17 09:17:56 -07:00
|
|
|
Burn
|
2018-08-13 12:24:15 -07:00
|
|
|
</button>
|
|
|
|
<div className="vote-scale--container">
|
2018-09-17 09:17:56 -07:00
|
|
|
<p className="vote-scale--votes">{this.votesBurnNumber} Votes</p>
|
|
|
|
<p className="vote-scale--percentage">{this.votesBurnPercents}%</p>
|
2018-08-13 12:24:15 -07:00
|
|
|
<div className={voteScaleClass}>
|
|
|
|
<div
|
2018-09-17 09:17:56 -07:00
|
|
|
className="vote-scale--fill vote-scale--fill_burn"
|
|
|
|
style={{ width: `${this.votesBurnPercents}%` }}
|
2018-08-13 12:24:15 -07:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="ballots-i-scale-column ballots-i-scale-column-3">
|
|
|
|
<button
|
|
|
|
type="button"
|
2018-09-17 09:17:56 -07:00
|
|
|
onClick={e => this.vote({ choice: FREEZE })}
|
|
|
|
className="btn btn-freeze ballots-i--vote_btn xl m-r-20"
|
2018-08-13 12:24:15 -07:00
|
|
|
>
|
2018-09-17 09:17:56 -07:00
|
|
|
Freeze
|
2018-08-13 12:24:15 -07:00
|
|
|
</button>
|
|
|
|
<div className="vote-scale--container">
|
2018-09-17 09:17:56 -07:00
|
|
|
<p className="vote-scale--votes">{this.votesFreezeNumber} Votes</p>
|
|
|
|
<p className="vote-scale--percentage">{this.votesFreezePercents}%</p>
|
2018-08-13 12:24:15 -07:00
|
|
|
<div className={voteScaleClass}>
|
|
|
|
<div
|
2018-09-17 09:17:56 -07:00
|
|
|
className="vote-scale--fill vote-scale--fill_freeze"
|
|
|
|
style={{ width: `${this.votesFreezePercents}%` }}
|
2018-08-13 12:24:15 -07:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="ballots-i-scale-column ballots-i-scale-column-3">
|
|
|
|
<button
|
2018-09-17 09:17:56 -07:00
|
|
|
className="btn btn-success ballots-i--vote_btn xl m-r-20"
|
|
|
|
onClick={e => this.vote({ choice: SEND })}
|
2018-08-13 12:24:15 -07:00
|
|
|
type="button"
|
|
|
|
>
|
2018-09-17 09:17:56 -07:00
|
|
|
Send
|
2018-08-13 12:24:15 -07:00
|
|
|
</button>
|
|
|
|
<div className="vote-scale--container">
|
2018-09-17 09:17:56 -07:00
|
|
|
<p className="vote-scale--votes">{this.votesSendNumber} Votes</p>
|
|
|
|
<p className="vote-scale--percentage">{this.votesSendPercents}%</p>
|
2018-08-13 12:24:15 -07:00
|
|
|
<div className={voteScaleClass}>
|
|
|
|
<div
|
2018-09-17 09:17:56 -07:00
|
|
|
className="vote-scale--fill vote-scale--fill_send"
|
|
|
|
style={{ width: `${this.votesSendPercents}%` }}
|
2018-08-13 12:24:15 -07:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2018-09-17 09:17:56 -07:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
votingScale = (
|
2018-08-16 07:50:52 -07:00
|
|
|
<div className="ballots-i-scale">
|
2018-07-13 07:32:22 -07:00
|
|
|
<div className="ballots-i-scale-column">
|
|
|
|
<button
|
|
|
|
type="button"
|
|
|
|
onClick={e => this.vote({ choice: REJECT })}
|
2018-08-13 12:24:15 -07:00
|
|
|
className="btn btn-danger ballots-i--vote_btn m-r-20"
|
2018-07-13 07:32:22 -07:00
|
|
|
>
|
|
|
|
No
|
|
|
|
</button>
|
|
|
|
<div className="vote-scale--container">
|
2018-08-09 07:16:29 -07:00
|
|
|
<p className="vote-scale--votes">{this.votesAgainstNumber} Votes</p>
|
2018-07-13 07:32:22 -07:00
|
|
|
<p className="vote-scale--percentage">{this.votesAgainstPercents}%</p>
|
|
|
|
<div className={voteScaleClass}>
|
|
|
|
<div
|
2018-08-13 11:29:25 -07:00
|
|
|
className="vote-scale--fill vote-scale--fill_no"
|
2018-07-13 07:32:22 -07:00
|
|
|
style={{ width: `${this.votesAgainstPercents}%` }}
|
|
|
|
/>
|
2018-01-11 10:49:08 -08:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2018-08-13 12:24:15 -07:00
|
|
|
<div className="ballots-i-scale-column reverse-responsive">
|
2018-07-13 07:32:22 -07:00
|
|
|
<div className="vote-scale--container">
|
2018-08-09 07:16:29 -07:00
|
|
|
<p className="vote-scale--votes">{this.votesForNumber} Votes</p>
|
2018-07-13 07:32:22 -07:00
|
|
|
<p className="vote-scale--percentage">{this.votesForPercents}%</p>
|
|
|
|
<div className={voteScaleClass}>
|
2018-08-13 11:29:25 -07:00
|
|
|
<div className="vote-scale--fill vote-scale--fill_yes" style={{ width: `${this.votesForPercents}%` }} />
|
2018-07-13 07:32:22 -07:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<button
|
2018-08-13 12:24:15 -07:00
|
|
|
className="btn btn-success ballots-i--vote_btn m-l-20"
|
2018-07-13 07:32:22 -07:00
|
|
|
onClick={e => this.vote({ choice: ACCEPT })}
|
2018-07-17 18:27:05 -07:00
|
|
|
type="button"
|
2018-07-13 07:32:22 -07:00
|
|
|
>
|
|
|
|
Yes
|
|
|
|
</button>
|
|
|
|
</div>
|
2018-08-16 07:50:52 -07:00
|
|
|
</div>
|
2018-09-17 09:17:56 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<div className={ballotClass}>
|
|
|
|
<div className="ballots-about">
|
|
|
|
<div className="ballots-about-i ballots-about-i_name">
|
|
|
|
<div className="ballots-about-td ballots-about-td-title">
|
|
|
|
<p className="ballots-about-i--title">Proposer</p>
|
|
|
|
</div>
|
|
|
|
<div className="ballots-about-td ballots-about-td-value">
|
|
|
|
<p className="ballots-i--name">{this.creator}</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{children}
|
|
|
|
<div className="ballots-about-i ballots-about-i_time">
|
|
|
|
<div className="ballots-about-td ballots-about-td-title">
|
|
|
|
<p className="ballots-about-i--title">Ballot Time</p>
|
|
|
|
</div>
|
|
|
|
<div className="ballots-about-td ballots-about-td-value">
|
|
|
|
<p className="ballots-i--created">{this.startTime}</p>
|
|
|
|
<p className="ballots-i--time">
|
|
|
|
{this.timeTo.displayValue} ({this.timeTo.title})
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{votingScale}
|
2018-08-02 12:14:11 -07:00
|
|
|
<div className="info-container">
|
|
|
|
<div className="info info-minimum">
|
|
|
|
Minimum {threshold} from {contractsStore.validatorsLength} validators are required to pass the proposal
|
|
|
|
</div>
|
|
|
|
<div className={`info info-details ${this.state.detailsCollapsed ? 'collapsed' : ''}`}>
|
|
|
|
{this.state.detailsCollapsed
|
|
|
|
? this.memo.substr(0, this.memo.lastIndexOf(' ', maxDetailsLength))
|
|
|
|
: this.memo}
|
|
|
|
{toggleShowMore}
|
|
|
|
</div>
|
2018-07-13 07:32:22 -07:00
|
|
|
</div>
|
|
|
|
<div className="ballots-footer">
|
|
|
|
<div className="ballots-footer-left">
|
2018-09-19 00:54:31 -07:00
|
|
|
<button type="button" onClick={e => this.cancelOrFinalize(e)} className={this.cancelOrFinalizeButtonClass}>
|
|
|
|
{this.cancelOrFinalizeButtonDisplayName}
|
2018-07-13 07:32:22 -07:00
|
|
|
</button>
|
2018-09-19 00:54:31 -07:00
|
|
|
<p>{this.cancelOrFinalizeDescription}</p>
|
2018-07-13 07:32:22 -07:00
|
|
|
</div>
|
|
|
|
{showHasAlreadyVotedLabel}
|
2018-07-17 18:27:05 -07:00
|
|
|
<div className="ballots-i--vote-label">
|
2018-07-13 07:32:22 -07:00
|
|
|
{this.typeName(votingType)} Ballot ID: {this.props.id}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|