Refactor votingRoom updating (#83)

This commit is contained in:
Szymon Szlachtowicz 2021-09-21 19:25:34 +02:00 committed by GitHub
parent e3e608e922
commit bd6f195b98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 285 additions and 112 deletions

View File

@ -22,6 +22,7 @@ contract VotingContract {
string description; string description;
uint256 totalVotesFor; uint256 totalVotesFor;
uint256 totalVotesAgainst; uint256 totalVotesAgainst;
uint256 id;
address[] voters; address[] voters;
} }
mapping(uint256 => mapping(address => bool)) private voted; mapping(uint256 => mapping(address => bool)) private voted;
@ -52,6 +53,46 @@ contract VotingContract {
); );
} }
function getVotingRoomLength() public view returns (uint256) {
return votingRooms.length;
}
function getLastNVotingRooms(uint256 amount) public view returns (VotingRoom[] memory result) {
if (amount == 0) {
return new VotingRoom[](0);
}
uint256 votingRoomsLen = votingRooms.length;
uint256 limit;
if (amount > votingRoomsLen) {
limit = 0;
} else {
limit = votingRoomsLen - amount;
}
uint256 i = votingRoomsLen;
uint256 j = 0;
result = new VotingRoom[](amount);
while (i > 0 && i > limit) {
result[j++] = votingRooms[--i];
}
if (j < amount) {
assembly {
mstore(result, sub(mload(result), sub(amount, j)))
}
}
}
function getVotingRoomsFrom(uint256 id) public view returns (VotingRoom[] memory result) {
if (id + 1 > votingRooms.length) {
return new VotingRoom[](0);
}
result = new VotingRoom[](votingRooms.length - id);
uint256 i = id;
uint256 j = 0;
while (i < votingRooms.length) {
result[j++] = votingRooms[i++];
}
}
function getVotingRooms() public view returns (VotingRoom[] memory) { function getVotingRooms() public view returns (VotingRoom[] memory) {
return votingRooms; return votingRooms;
} }
@ -87,6 +128,7 @@ contract VotingContract {
newVotingRoom.question = question; newVotingRoom.question = question;
newVotingRoom.description = description; newVotingRoom.description = description;
newVotingRoom.totalVotesFor = voteAmount; newVotingRoom.totalVotesFor = voteAmount;
newVotingRoom.id = votingRooms.length;
voted[votingRooms.length][msg.sender] = true; voted[votingRooms.length][msg.sender] = true;
votingRooms.push(newVotingRoom); votingRooms.push(newVotingRoom);

View File

@ -137,17 +137,19 @@ describe('Contract', () => {
it('gets', async () => { it('gets', async () => {
const { contract } = await loadFixture(fixture) const { contract } = await loadFixture(fixture)
await contract.initializeVotingRoom('T1', 'short desc', BigNumber.from(100)) await contract.initializeVotingRoom('T1', 'short desc', BigNumber.from(100))
const votingRoom0 = await contract.votingRooms(0)
expect((await contract.votingRooms(0))[2]).to.eq('T1') expect(votingRoom0[2]).to.eq('T1')
expect((await contract.votingRooms(0))[3]).to.eq('short desc') expect(votingRoom0[3]).to.eq('short desc')
expect((await contract.votingRooms(0))[4]).to.deep.eq(BigNumber.from(100)) expect(votingRoom0[4]).to.deep.eq(BigNumber.from(100))
expect((await contract.votingRooms(0))[5]).to.deep.eq(BigNumber.from(0)) expect(votingRoom0[5]).to.deep.eq(BigNumber.from(0))
expect(votingRoom0[6]).to.deep.eq(BigNumber.from(0))
await contract.initializeVotingRoom('T2', 'long desc', BigNumber.from(200)) await contract.initializeVotingRoom('T2', 'long desc', BigNumber.from(200))
expect((await contract.votingRooms(1))[2]).to.eq('T2') expect((await contract.votingRooms(1))[2]).to.eq('T2')
expect((await contract.votingRooms(1))[3]).to.eq('long desc') expect((await contract.votingRooms(1))[3]).to.eq('long desc')
expect((await contract.votingRooms(1))[4]).to.deep.eq(BigNumber.from(200)) expect((await contract.votingRooms(1))[4]).to.deep.eq(BigNumber.from(200))
expect((await contract.votingRooms(1))[5]).to.deep.eq(BigNumber.from(0)) expect((await contract.votingRooms(1))[5]).to.deep.eq(BigNumber.from(0))
expect((await contract.votingRooms(1))[6]).to.deep.eq(BigNumber.from(1))
}) })
it('reverts no room', async () => { it('reverts no room', async () => {
@ -190,6 +192,130 @@ describe('Contract', () => {
}) })
}) })
describe('helpers', () => { describe('helpers', () => {
describe('getLastNVotingRooms', () => {
it('get 0 voting empty', async () => {
const { contract } = await loadFixture(fixture)
expect((await contract.getLastNVotingRooms(0)).length).to.eq(0)
})
it('get 1 voting empty', async () => {
const { contract } = await loadFixture(fixture)
expect((await contract.getLastNVotingRooms(1)).length).to.eq(0)
})
it('get 1 voting 1', async () => {
const { contract } = await loadFixture(fixture)
await contract.initializeVotingRoom('T1', 't1', BigNumber.from(100))
expect((await contract.getLastNVotingRooms(1)).length).to.eq(1)
})
it('get 5 voting empty', async () => {
const { contract } = await loadFixture(fixture)
expect((await contract.getLastNVotingRooms(5)).length).to.eq(0)
})
it('get 5 voting 1', async () => {
const { contract } = await loadFixture(fixture)
await contract.initializeVotingRoom('T1', 't1', BigNumber.from(100))
expect((await contract.getLastNVotingRooms(5)).length).to.eq(1)
})
it('get 5 voting 2', async () => {
const { contract } = await loadFixture(fixture)
await contract.initializeVotingRoom('T1', 't1', BigNumber.from(100))
await contract.initializeVotingRoom('T2', 't2', BigNumber.from(200))
expect((await contract.getLastNVotingRooms(5)).length).to.eq(2)
})
it('get 5 voting 4', async () => {
const { contract } = await loadFixture(fixture)
await contract.initializeVotingRoom('T1', 't1', BigNumber.from(100))
await contract.initializeVotingRoom('T2', 't2', BigNumber.from(200))
await contract.initializeVotingRoom('T3', 't3', BigNumber.from(200))
await contract.initializeVotingRoom('T4', 't4', BigNumber.from(200))
expect((await contract.getLastNVotingRooms(5)).length).to.eq(4)
})
it('get 5 voting 5', async () => {
const { contract } = await loadFixture(fixture)
await contract.initializeVotingRoom('T1', 't1', BigNumber.from(100))
await contract.initializeVotingRoom('T2', 't2', BigNumber.from(200))
await contract.initializeVotingRoom('T3', 't3', BigNumber.from(200))
await contract.initializeVotingRoom('T4', 't4', BigNumber.from(200))
await contract.initializeVotingRoom('T5', 't5', BigNumber.from(200))
expect((await contract.getLastNVotingRooms(5)).length).to.eq(5)
})
it('get 5 voting 6', async () => {
const { contract } = await loadFixture(fixture)
await contract.initializeVotingRoom('T1', 't1', BigNumber.from(100))
await contract.initializeVotingRoom('T2', 't2', BigNumber.from(200))
await contract.initializeVotingRoom('T3', 't3', BigNumber.from(200))
await contract.initializeVotingRoom('T4', 't4', BigNumber.from(200))
await contract.initializeVotingRoom('T5', 't5', BigNumber.from(200))
await contract.initializeVotingRoom('T6', 't6', BigNumber.from(200))
await contract.initializeVotingRoom('T7', 't7', BigNumber.from(200))
expect((await contract.getLastNVotingRooms(5)).length).to.eq(5)
})
it('get 5 voting 7', async () => {
const { contract } = await loadFixture(fixture)
await contract.initializeVotingRoom('T1', 't1', BigNumber.from(100))
await contract.initializeVotingRoom('T2', 't2', BigNumber.from(200))
await contract.initializeVotingRoom('T3', 't3', BigNumber.from(200))
await contract.initializeVotingRoom('T4', 't4', BigNumber.from(200))
await contract.initializeVotingRoom('T5', 't5', BigNumber.from(200))
await contract.initializeVotingRoom('T6', 't6', BigNumber.from(200))
await contract.initializeVotingRoom('T7', 't7', BigNumber.from(200))
expect((await contract.getLastNVotingRooms(5)).length).to.eq(5)
})
})
describe('getVotingRoomsFrom', () => {
it('empty', async () => {
const { contract } = await loadFixture(fixture)
expect((await contract.getVotingRoomsFrom(1)).length).to.eq(0)
})
it('from 1 votingRooms 1', async () => {
const { contract } = await loadFixture(fixture)
await contract.initializeVotingRoom('T1', 't1', BigNumber.from(100))
expect((await contract.getVotingRoomsFrom(1)).length).to.eq(0)
})
it('from 1 votingRooms 2', async () => {
const { contract } = await loadFixture(fixture)
await contract.initializeVotingRoom('T1', 't1', BigNumber.from(100))
await contract.initializeVotingRoom('T2', 't2', BigNumber.from(200))
expect((await contract.getVotingRoomsFrom(1)).length).to.eq(1)
})
it('from 1 votingRooms 3', async () => {
const { contract } = await loadFixture(fixture)
await contract.initializeVotingRoom('T1', 't1', BigNumber.from(100))
await contract.initializeVotingRoom('T2', 't2', BigNumber.from(200))
await contract.initializeVotingRoom('T3', 't3', BigNumber.from(200))
expect((await contract.getVotingRoomsFrom(1)).length).to.eq(2)
})
it('from 0 votingRooms 0', async () => {
const { contract } = await loadFixture(fixture)
expect((await contract.getVotingRoomsFrom(0)).length).to.eq(0)
})
it('from 0 votingRooms 1', async () => {
const { contract } = await loadFixture(fixture)
await contract.initializeVotingRoom('T1', 't1', BigNumber.from(100))
expect((await contract.getVotingRoomsFrom(0)).length).to.eq(1)
})
it('from 0 votingRooms 2', async () => {
const { contract } = await loadFixture(fixture)
await contract.initializeVotingRoom('T1', 't1', BigNumber.from(100))
await contract.initializeVotingRoom('T2', 't2', BigNumber.from(100))
expect((await contract.getVotingRoomsFrom(0)).length).to.eq(2)
})
})
it('get voting rooms', async () => { it('get voting rooms', async () => {
const { contract } = await loadFixture(fixture) const { contract } = await loadFixture(fixture)
await contract.initializeVotingRoom('T1', 't1', BigNumber.from(100)) await contract.initializeVotingRoom('T1', 't1', BigNumber.from(100))

View File

@ -55,7 +55,7 @@ export class WakuVoting extends WakuMessaging {
const votingContract = new Contract(contractAddress, VotingContract.abi, provider) const votingContract = new Contract(contractAddress, VotingContract.abi, provider)
const tokenAddress = await votingContract.token() const tokenAddress = await votingContract.token()
const providerName = (await provider.getNetwork()).name const providerName = (await provider.getNetwork()).name
return new WakuVoting( const wakuVoting = new WakuVoting(
appName, appName,
votingContract, votingContract,
tokenAddress, tokenAddress,
@ -65,6 +65,7 @@ export class WakuVoting extends WakuMessaging {
providerName, providerName,
waku waku
) )
return wakuVoting
} }
public async createVote(question: string, descripiton: string, tokenAmount: BigNumber) { public async createVote(question: string, descripiton: string, tokenAmount: BigNumber) {
@ -77,26 +78,39 @@ export class WakuVoting extends WakuMessaging {
private lastPolls: VotingRoom[] = [] private lastPolls: VotingRoom[] = []
private lastGetPollsBlockNumber = 0 private lastGetPollsBlockNumber = 0
private lastActivePoll = 0
public async getVotingRooms() { public async getVotingRooms() {
const blockNumber = await this.provider.getBlockNumber() const blockNumber = await this.provider.getBlockNumber()
if (blockNumber != this.lastGetPollsBlockNumber) { if (blockNumber != this.lastGetPollsBlockNumber) {
this.lastGetPollsBlockNumber = blockNumber this.lastGetPollsBlockNumber = blockNumber
const polls = await this.votingContract.getVotingRooms()
this.lastPolls = polls.map((poll: any, idx: number): VotingRoom => { this.lastActivePoll = this.lastPolls.findIndex((poll) => poll.timeLeft > 0)
const timeLeft = poll[1].toNumber() - Date.now() / 1000 if (this.lastActivePoll < 0) {
return { this.lastActivePoll = this.lastPolls.length
}
const polls = await this.votingContract.getVotingRoomsFrom(this.lastActivePoll)
polls.forEach((poll: any) => {
const timeLeft = poll[1].toNumber() * 1000 - Date.now()
const votingRoom: VotingRoom = {
startBlock: poll[0], startBlock: poll[0],
endAt: poll[1], endAt: poll[1],
question: poll[2], question: poll[2],
description: poll[3], description: poll[3],
totalVotesFor: poll[4], totalVotesFor: poll[4],
totalVotesAgainst: poll[5], totalVotesAgainst: poll[5],
voters: poll[6], wakuTotalVotesFor: poll[4],
id: idx, wakuTotalVotesAgainst: poll[5],
voters: poll[7],
id: BigNumber.from(poll[6]).toNumber(),
timeLeft, timeLeft,
voteWinner: timeLeft <= 0 ? (poll[5].gt(poll[4]) ? 1 : 2) : undefined, voteWinner: timeLeft <= 0 ? (poll[5].gt(poll[4]) ? 1 : 2) : undefined,
} }
if (this.lastPolls[votingRoom.id]) {
this.lastPolls[votingRoom.id] = votingRoom
} else {
this.lastPolls.push(votingRoom)
}
}) })
await Promise.all( await Promise.all(
this.lastPolls.map(async (poll) => { this.lastPolls.map(async (poll) => {
@ -112,15 +126,6 @@ export class WakuVoting extends WakuMessaging {
return this.lastPolls.slice().reverse() return this.lastPolls.slice().reverse()
} }
public async getVotingRoom(id: number) {
try {
await this.getVotingRooms()
return this.lastPolls[id]
} catch {
return undefined
}
}
public async sendVote(roomId: number, selectedAnswer: number, tokenAmount: BigNumber) { public async sendVote(roomId: number, selectedAnswer: number, tokenAmount: BigNumber) {
const signer = this.provider.getSigner() const signer = this.provider.getSigner()
const vote = await VoteMsg._createWithSignFunction( const vote = await VoteMsg._createWithSignFunction(
@ -144,15 +149,26 @@ export class WakuVoting extends WakuMessaging {
this.votingContract.castVotes(mappedVotes) this.votingContract.castVotes(mappedVotes)
} }
public async getRoomWakuVotes(id: number) { public async getVotingRoom(id: number) {
await this.updateBalances() await this.updateBalances()
const votingRoom = await this.getVotingRoom(id) let votingRoom: VotingRoom
if (!votingRoom || votingRoom.timeLeft < 0) { try {
await this.getVotingRooms()
votingRoom = this.lastPolls[id]
votingRoom.wakuVotes = undefined
votingRoom.wakuTotalVotesAgainst = votingRoom.totalVotesAgainst
votingRoom.wakuTotalVotesFor = votingRoom.totalVotesFor
} catch {
return undefined return undefined
} }
if (!votingRoom) {
return undefined
}
if (votingRoom.timeLeft < 0) {
return votingRoom
}
const votersHashMap: { [voter: string]: boolean } = {} const votersHashMap: { [voter: string]: boolean } = {}
votingRoom.voters.forEach((voter) => (votersHashMap[voter] = true)) votingRoom.voters.forEach((voter) => (votersHashMap[voter] = true))
const newVotingRoom: VotingRoom = { ...votingRoom }
const wakuVotes = this.wakuMessages['vote'].arr.filter((vote: VoteMsg) => { const wakuVotes = this.wakuMessages['vote'].arr.filter((vote: VoteMsg) => {
if ( if (
vote.roomId === id && vote.roomId === id &&
@ -162,9 +178,9 @@ export class WakuVoting extends WakuMessaging {
if (!votersHashMap[vote.voter]) { if (!votersHashMap[vote.voter]) {
votersHashMap[vote.voter] = true votersHashMap[vote.voter] = true
if (vote.answer === 0) { if (vote.answer === 0) {
newVotingRoom.totalVotesAgainst = newVotingRoom.totalVotesAgainst.add(vote.tokenAmount) votingRoom.wakuTotalVotesAgainst = votingRoom.wakuTotalVotesAgainst.add(vote.tokenAmount)
} else { } else {
newVotingRoom.totalVotesFor = newVotingRoom.totalVotesFor.add(vote.tokenAmount) votingRoom.wakuTotalVotesFor = votingRoom.wakuTotalVotesFor.add(vote.tokenAmount)
} }
return true return true
} }
@ -173,6 +189,7 @@ export class WakuVoting extends WakuMessaging {
}) as VoteMsg[] }) as VoteMsg[]
const sum = wakuVotes.reduce((prev, curr) => prev.add(curr.tokenAmount), BigNumber.from(0)) const sum = wakuVotes.reduce((prev, curr) => prev.add(curr.tokenAmount), BigNumber.from(0))
return { sum, wakuVotes, newVotingRoom } votingRoom.wakuVotes = { sum, votes: wakuVotes }
return votingRoom
} }
} }

View File

@ -1,4 +1,5 @@
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
import { VoteMsg } from '../models/VoteMsg'
export enum PollType { export enum PollType {
WEIGHTED = 0, WEIGHTED = 0,
@ -12,6 +13,12 @@ export type VotingRoom = {
description: string description: string
totalVotesFor: BigNumber totalVotesFor: BigNumber
totalVotesAgainst: BigNumber totalVotesAgainst: BigNumber
wakuTotalVotesFor: BigNumber
wakuTotalVotesAgainst: BigNumber
wakuVotes?: {
sum: BigNumber
votes: VoteMsg[]
}
voters: string[] voters: string[]
id: number id: number
timeLeft: number timeLeft: number

View File

@ -12,17 +12,17 @@ import { NewVoteModal } from './newVoteModal/NewVoteModal'
import { useEthers } from '@usedapp/core' import { useEthers } from '@usedapp/core'
import { Modal, Networks, useMobileVersion, Theme } from '@status-waku-voting/react-components' import { Modal, Networks, useMobileVersion, Theme } from '@status-waku-voting/react-components'
import { useHistory } from 'react-router' import { useHistory } from 'react-router'
import { useVotingRooms } from '@status-waku-voting/proposal-hooks' import { useVotingRoomsId } from '@status-waku-voting/proposal-hooks'
type ProposalListHeaderProps = { type ProposalListHeaderProps = {
votes: VotingRoom[] votesLength: number
theme: Theme theme: Theme
wakuVoting: WakuVoting wakuVoting: WakuVoting
tokenBalance: number tokenBalance: number
account: string | null | undefined account: string | null | undefined
} }
function ProposalListHeader({ votes, theme, wakuVoting, tokenBalance, account }: ProposalListHeaderProps) { function ProposalListHeader({ votesLength, theme, wakuVoting, tokenBalance, account }: ProposalListHeaderProps) {
const [showNewVoteModal, setShowNewVoteModal] = useState(false) const [showNewVoteModal, setShowNewVoteModal] = useState(false)
const [showConnectionModal, setShowConnectionModal] = useState(false) const [showConnectionModal, setShowConnectionModal] = useState(false)
const { activateBrowserWallet } = useEthers() const { activateBrowserWallet } = useEthers()
@ -54,7 +54,7 @@ function ProposalListHeader({ votes, theme, wakuVoting, tokenBalance, account }:
<Networks /> <Networks />
</Modal> </Modal>
)} )}
{votes?.length === 0 ? ( {votesLength === 0 ? (
<VotingEmpty account={account} theme={theme} onConnectClick={onConnectClick} onCreateClick={onCreateClick} /> <VotingEmpty account={account} theme={theme} onConnectClick={onConnectClick} onCreateClick={onCreateClick} />
) : ( ) : (
<ProposalHeader account={account} theme={theme} onConnectClick={onConnectClick} onCreateClick={onCreateClick} /> <ProposalHeader account={account} theme={theme} onConnectClick={onConnectClick} onCreateClick={onCreateClick} />
@ -69,14 +69,14 @@ type ProposalProps = {
} }
export function Proposal({ wakuVoting, account }: ProposalProps) { export function Proposal({ wakuVoting, account }: ProposalProps) {
const votes = useVotingRooms(wakuVoting) const votes = useVotingRoomsId(wakuVoting)
const tokenBalance = useTokenBalance(account, wakuVoting) const tokenBalance = useTokenBalance(account, wakuVoting)
return ( return (
<ProposalWrapper> <ProposalWrapper>
<ProposalVotesWrapper> <ProposalVotesWrapper>
<ProposalListHeader <ProposalListHeader
votes={votes} votesLength={votes.length}
tokenBalance={tokenBalance} tokenBalance={tokenBalance}
theme={blueTheme} theme={blueTheme}
account={account} account={account}

View File

@ -6,9 +6,10 @@ import { ProposalInfo } from './ProposalInfo'
import { ProposalVote } from './ProposalVoteCard/ProposalVote' import { ProposalVote } from './ProposalVoteCard/ProposalVote'
import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType' import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType'
import { WakuVoting } from '@status-waku-voting/core' import { WakuVoting } from '@status-waku-voting/core'
import { useVotingRoom } from '@status-waku-voting/proposal-hooks'
interface ProposalCardProps { interface ProposalCardProps {
votingRoom: VotingRoom votingRoomId: number
mobileVersion?: boolean mobileVersion?: boolean
theme: Theme theme: Theme
hideModalFunction?: (val: boolean) => void hideModalFunction?: (val: boolean) => void
@ -20,12 +21,16 @@ interface ProposalCardProps {
export function ProposalCard({ export function ProposalCard({
account, account,
theme, theme,
votingRoom, votingRoomId,
mobileVersion, mobileVersion,
availableAmount, availableAmount,
wakuVoting, wakuVoting,
}: ProposalCardProps) { }: ProposalCardProps) {
const history = useHistory() const history = useHistory()
const votingRoom = useVotingRoom(votingRoomId, wakuVoting)
if (!votingRoom) {
return <></>
}
return ( return (
<Card onClick={() => mobileVersion && history.push(`/votingRoom/${votingRoom.id.toString()}`)}> <Card onClick={() => mobileVersion && history.push(`/votingRoom/${votingRoom.id.toString()}`)}>

View File

@ -9,7 +9,7 @@ import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType
type ProposalListProps = { type ProposalListProps = {
theme: Theme theme: Theme
wakuVoting: WakuVoting wakuVoting: WakuVoting
votes: VotingRoom[] votes: number[]
availableAmount: number availableAmount: number
account: string | null | undefined account: string | null | undefined
} }
@ -22,9 +22,9 @@ export function ProposalList({ theme, wakuVoting, votes, availableAmount, accoun
return ( return (
<ProposalCard <ProposalCard
account={account} account={account}
votingRoom={votingRoom} votingRoomId={votingRoom}
theme={theme} theme={theme}
key={votingRoom.id} key={votingRoom}
mobileVersion={mobileVersion} mobileVersion={mobileVersion}
availableAmount={availableAmount} availableAmount={availableAmount}
wakuVoting={wakuVoting} wakuVoting={wakuVoting}

View File

@ -9,7 +9,6 @@ import { VoteModal } from '../VoteModal'
import { VoteAnimatedModal } from '../VoteAnimatedModal' import { VoteAnimatedModal } from '../VoteAnimatedModal'
import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType' import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType'
import { WakuVoting } from '@status-waku-voting/core' import { WakuVoting } from '@status-waku-voting/core'
import { useRoomWakuVotes } from '@status-waku-voting/proposal-hooks'
interface ProposalVoteProps { interface ProposalVoteProps {
theme: Theme theme: Theme
@ -54,14 +53,12 @@ export function ProposalVote({
setShowConfirmModal(val) setShowConfirmModal(val)
} }
const { votes, sum, modifiedVotingRoom } = useRoomWakuVotes(votingRoom, wakuVoting)
return ( return (
<Card> <Card>
{showVoteModal && ( {showVoteModal && (
<Modal heading={votingRoom.question} setShowModal={setShowVoteModal} theme={theme}> <Modal heading={votingRoom.question} setShowModal={setShowVoteModal} theme={theme}>
<VoteModal <VoteModal
votingRoom={modifiedVotingRoom ?? votingRoom} votingRoom={votingRoom}
availableAmount={availableAmount} availableAmount={availableAmount}
selectedVote={selectedVoted} selectedVote={selectedVoted}
proposingAmount={proposingAmount} proposingAmount={proposingAmount}
@ -87,7 +84,7 @@ export function ProposalVote({
<CardHeading /> <CardHeading />
)} )}
<VoteChart votingRoom={modifiedVotingRoom ?? votingRoom} selectedVote={selectedVoted} /> <VoteChart votingRoom={votingRoom} selectedVote={selectedVoted} />
<CardButtons> <CardButtons>
{votingRoom.voteWinner ? ( {votingRoom.voteWinner ? (
@ -121,7 +118,11 @@ export function ProposalVote({
{' '} {' '}
<ViewLink address={'#'} /> <ViewLink address={'#'} />
</CardViewLink> </CardViewLink>
<VoteSubmitButton votes={sum.toNumber()} disabled={!account} onClick={() => wakuVoting.commitVotes(votes)} /> <VoteSubmitButton
votes={votingRoom?.wakuVotes?.sum.toNumber() ?? 0}
disabled={!account}
onClick={() => wakuVoting.commitVotes(votingRoom?.wakuVotes?.votes ?? [])}
/>
</CardVoteBottom> </CardVoteBottom>
</Card> </Card>
) )

View File

@ -24,10 +24,10 @@ export function VoteChart({ votingRoom, proposingAmount, selectedVote, isAnimati
const mobileVersion = useMobileVersion(ref, 600) const mobileVersion = useMobileVersion(ref, 600)
const voteSum = useMemo( const voteSum = useMemo(
() => votingRoom.totalVotesFor.add(votingRoom.totalVotesAgainst), () => votingRoom.wakuTotalVotesFor.add(votingRoom.wakuTotalVotesAgainst),
[votingRoom.totalVotesFor.toString(), votingRoom.totalVotesAgainst.toString()] [votingRoom.wakuTotalVotesFor.toString(), votingRoom.wakuTotalVotesAgainst.toString()]
) )
const graphWidth = useMemo(() => votingRoom.totalVotesAgainst.mul(100).div(voteSum).toNumber(), [voteSum]) const graphWidth = useMemo(() => votingRoom.wakuTotalVotesAgainst.mul(100).div(voteSum).toNumber(), [voteSum])
const balanceWidth = useMemo(() => { const balanceWidth = useMemo(() => {
if (!proposingAmount) { if (!proposingAmount) {
@ -35,12 +35,11 @@ export function VoteChart({ votingRoom, proposingAmount, selectedVote, isAnimati
} else { } else {
const divider = voteSum.add(proposingAmount) const divider = voteSum.add(proposingAmount)
return selectedVote === 0 return selectedVote === 0
? votingRoom.totalVotesAgainst.add(proposingAmount).mul(100).div(divider).toNumber() ? votingRoom.wakuTotalVotesAgainst.add(proposingAmount).mul(100).div(divider).toNumber()
: votingRoom.totalVotesAgainst.mul(100).div(divider).toNumber() : votingRoom.wakuTotalVotesAgainst.mul(100).div(divider).toNumber()
} }
}, [graphWidth, voteSum, proposingAmount]) }, [graphWidth, voteSum, proposingAmount])
const timeLeft = useMemo(() => votingRoom.timeLeft * 1000, [votingRoom.timeLeft])
const voteWinner = useMemo(() => votingRoom.voteWinner, [votingRoom.voteWinner]) const voteWinner = useMemo(() => votingRoom.voteWinner, [votingRoom.voteWinner])
return ( return (
<Votes ref={ref}> <Votes ref={ref}>
@ -48,16 +47,18 @@ export function VoteChart({ votingRoom, proposingAmount, selectedVote, isAnimati
<VoteBox <VoteBox
voteType={2} voteType={2}
mobileVersion={mobileVersion} mobileVersion={mobileVersion}
totalVotes={votingRoom.totalVotesAgainst.toNumber()} totalVotes={votingRoom.wakuTotalVotesAgainst.toNumber()}
won={voteWinner === 2} won={voteWinner === 2}
selected={isAnimation && selectedVote === 0} selected={isAnimation && selectedVote === 0}
proposingAmount={proposingAmount} proposingAmount={proposingAmount}
/> />
{!voteWinner && <TimeLeft className={selectedVote ? '' : 'notModal'}>{formatTimeLeft(timeLeft)}</TimeLeft>} {!voteWinner && (
<TimeLeft className={selectedVote ? '' : 'notModal'}>{formatTimeLeft(votingRoom.timeLeft)}</TimeLeft>
)}
<VoteBox <VoteBox
voteType={1} voteType={1}
mobileVersion={mobileVersion} mobileVersion={mobileVersion}
totalVotes={votingRoom.totalVotesFor.toNumber()} totalVotes={votingRoom.wakuTotalVotesFor.toNumber()}
won={voteWinner === 1} won={voteWinner === 1}
selected={isAnimation && selectedVote === 1} selected={isAnimation && selectedVote === 1}
proposingAmount={proposingAmount} proposingAmount={proposingAmount}
@ -70,7 +71,9 @@ export function VoteChart({ votingRoom, proposingAmount, selectedVote, isAnimati
voteWinner={voteWinner} voteWinner={voteWinner}
isAnimation={isAnimation} isAnimation={isAnimation}
/> />
<TimeLeftMobile className={selectedVote ? '' : 'notModal'}>{formatTimeLeft(timeLeft)}</TimeLeftMobile> <TimeLeftMobile className={selectedVote ? '' : 'notModal'}>
{formatTimeLeft(votingRoom.timeLeft)}
</TimeLeftMobile>
</VoteGraphBarWrap> </VoteGraphBarWrap>
</Votes> </Votes>
) )

View File

@ -8,7 +8,7 @@ import { VoteChart } from '../ProposalVoteCard/VoteChart'
import { ProposalInfo } from '../ProposalInfo' import { ProposalInfo } from '../ProposalInfo'
import { VotePropose } from '../VotePropose' import { VotePropose } from '../VotePropose'
import { VotesBtns } from '../ProposalVoteCard/ProposalVote' import { VotesBtns } from '../ProposalVoteCard/ProposalVote'
import { useRoomWakuVotes, useVotingRoom } from '@status-waku-voting/proposal-hooks' import { useVotingRoom } from '@status-waku-voting/proposal-hooks'
import { WakuVoting } from '@status-waku-voting/core' import { WakuVoting } from '@status-waku-voting/core'
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
interface ProposalVoteMobileProps { interface ProposalVoteMobileProps {
@ -24,7 +24,6 @@ export function ProposalVoteMobile({ wakuVoting, availableAmount }: ProposalVote
const votingRoom = useVotingRoom(Number(id), wakuVoting) const votingRoom = useVotingRoom(Number(id), wakuVoting)
const voteWinner = useMemo(() => votingRoom?.voteWinner, [votingRoom?.voteWinner]) const voteWinner = useMemo(() => votingRoom?.voteWinner, [votingRoom?.voteWinner])
const { votes, sum, modifiedVotingRoom } = useRoomWakuVotes(votingRoom, wakuVoting)
if (!votingRoom) { if (!votingRoom) {
return <>Loading</> return <>Loading</>
@ -34,7 +33,7 @@ export function ProposalVoteMobile({ wakuVoting, availableAmount }: ProposalVote
<Card> <Card>
<ProposalInfo votingRoom={votingRoom} mobileMode={true} providerName={wakuVoting.providerName} /> <ProposalInfo votingRoom={votingRoom} mobileMode={true} providerName={wakuVoting.providerName} />
<VoteChartWrap> <VoteChartWrap>
<VoteChart votingRoom={modifiedVotingRoom ?? votingRoom} selectedVote={selectedVoted} /> <VoteChart votingRoom={votingRoom} selectedVote={selectedVoted} />
</VoteChartWrap> </VoteChartWrap>
{!voteWinner && ( {!voteWinner && (
<VotePropose <VotePropose
@ -73,7 +72,13 @@ export function ProposalVoteMobile({ wakuVoting, availableAmount }: ProposalVote
<CardVoteBottom> <CardVoteBottom>
{' '} {' '}
<VoteSubmitButton votes={sum.toNumber()} disabled={!account} onClick={() => wakuVoting.commitVotes(votes)} /> {votingRoom.wakuVotes && (
<VoteSubmitButton
votes={votingRoom.wakuVotes.sum.toNumber()}
disabled={!account}
onClick={() => wakuVoting.commitVotes(votingRoom?.wakuVotes?.votes ?? [])}
/>
)}
</CardVoteBottom> </CardVoteBottom>
</Card> </Card>
) )

View File

@ -1,35 +0,0 @@
import React, { useState, useRef, useEffect } from 'react'
import { WakuVoting } from '@status-waku-voting/core'
import { VoteMsg } from '@status-waku-voting/core/dist/esm/src/models/VoteMsg'
import { utils, BigNumber } from 'ethers'
import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType'
export function useRoomWakuVotes(votingRoom: VotingRoom | undefined, wakuVoting: WakuVoting) {
const [votes, setVotes] = useState<VoteMsg[]>([])
const [sum, setSum] = useState(BigNumber.from(0))
const [modifiedVotingRoom, setModifiedVotingRoom] = useState(votingRoom)
const hash = useRef('')
useEffect(() => {
const updateVotes = async () => {
if (!votingRoom) {
return
}
const newVotes = await wakuVoting.getRoomWakuVotes(votingRoom.id)
if (newVotes) {
const newHash = utils.id(newVotes.wakuVotes.map((vote) => vote.id).join(''))
if (newHash != hash.current) {
hash.current = newHash
setVotes(newVotes.wakuVotes)
setSum(newVotes.sum)
setModifiedVotingRoom(newVotes.newVotingRoom)
}
}
}
updateVotes()
const interval = setInterval(updateVotes, 1000)
return () => clearInterval(interval)
}, [wakuVoting, votingRoom])
return { votes, sum, modifiedVotingRoom }
}

View File

@ -1,18 +1,22 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState, useRef } from 'react'
import { WakuVoting } from '@status-waku-voting/core' import { WakuVoting } from '@status-waku-voting/core'
import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType' import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType'
export function useVotingRoom(id: number, wakuVoting: WakuVoting) { export function useVotingRoom(id: number, wakuVoting: WakuVoting) {
const [votingRoom, setVotingRoom] = useState<VotingRoom | undefined>(undefined) const [votingRoom, setVotingRoom] = useState<VotingRoom | undefined>(undefined)
const lastTimeLeft = useRef(1)
useEffect(() => { useEffect(() => {
const updateFunction = async () => { const updateFunction = async () => {
setVotingRoom(await wakuVoting.getVotingRoom(id)) if (lastTimeLeft.current > 0) {
const votingRoom = await wakuVoting.getVotingRoom(id)
setVotingRoom(votingRoom)
lastTimeLeft.current = votingRoom?.timeLeft ?? 1
}
} }
updateFunction() updateFunction()
const interval = setInterval(updateFunction, 10000) const interval = setInterval(updateFunction, 1000)
return () => clearInterval(interval) return () => clearInterval(interval)
}, [id]) }, [id, wakuVoting])
return votingRoom return votingRoom
} }

View File

@ -3,22 +3,21 @@ import { WakuVoting } from '@status-waku-voting/core'
import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType' import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType'
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
export function useVotingRooms(wakuVoting: WakuVoting) { export function useVotingRoomsId(wakuVoting: WakuVoting) {
const [votes, setVotes] = useState<VotingRoom[]>([]) const [votes, setVotes] = useState<number[]>([])
const hash = useRef('') const votesLength = useRef(0)
useEffect(() => { useEffect(() => {
const interval = setInterval(async () => { const interval = setInterval(async () => {
const newRooms = await wakuVoting.getVotingRooms() const newRooms = (await wakuVoting.getVotingRooms()).map((e) => e.id)
const newHash = id(newRooms.map((votingRoom) => votingRoom.id.toString()).join('')) if (newRooms.length != votesLength.current) {
if (newHash != hash.current) {
setVotes(newRooms) setVotes(newRooms)
hash.current = newHash votesLength.current = newRooms.length
} }
}, 10000) }, 10000)
setVotes([]) setVotes([])
wakuVoting.getVotingRooms().then((e) => { wakuVoting.getVotingRooms().then((e) => {
setVotes(e) setVotes(e.map((vote) => vote.id))
hash.current = id(e.map((votingRoom) => votingRoom.id.toString()).join('')) votesLength.current = e.length
}) })
return () => clearInterval(interval) return () => clearInterval(interval)
}, [wakuVoting]) }, [wakuVoting])

View File

@ -1,5 +1,4 @@
import { useWakuProposal } from './hooks/useWakuProposal' import { useWakuProposal } from './hooks/useWakuProposal'
import { useVotingRoom } from './hooks/useVotingRoom' import { useVotingRoom } from './hooks/useVotingRoom'
import { useVotingRooms } from './hooks/useVotingRooms' import { useVotingRoomsId } from './hooks/useVotingRoomsId'
import { useRoomWakuVotes } from './hooks/useRoomWakuVotes' export { useWakuProposal, useVotingRoom, useVotingRoomsId }
export { useWakuProposal, useVotingRoom, useVotingRooms, useRoomWakuVotes }

View File

@ -30,7 +30,7 @@ function Proposals() {
const config = useConfig() const config = useConfig()
const waku = useWakuProposal( const waku = useWakuProposal(
'test', 'test',
'0x5795A64A70cde4073DBa9EEBC5C6b675B15C815a', '0x965a61B9A91400AdA142b591ab0E235024c94E3D',
library, library,
config?.multicallAddresses?.[chainId ?? 1337] config?.multicallAddresses?.[chainId ?? 1337]
) )