Refactor votingCards (#71)

This commit is contained in:
Szymon Szlachtowicz 2021-09-14 23:16:05 +02:00 committed by GitHub
parent 98fb2ea515
commit 983f557fbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 112 additions and 158 deletions

View File

@ -73,6 +73,7 @@ export class WakuVoting extends WakuMessaging {
this.lastGetPollsBlockNumber = blockNumber this.lastGetPollsBlockNumber = blockNumber
const polls = await this.votingContract.getVotingRooms() const polls = await this.votingContract.getVotingRooms()
this.lastPolls = polls.map((poll: any, idx: number): VotingRoom => { this.lastPolls = polls.map((poll: any, idx: number): VotingRoom => {
const timeLeft = poll[1].toNumber() - Date.now() / 1000
return { return {
startBlock: poll[0], startBlock: poll[0],
endAt: poll[1], endAt: poll[1],
@ -82,12 +83,18 @@ export class WakuVoting extends WakuMessaging {
totalVotesAgainst: poll[5], totalVotesAgainst: poll[5],
voters: poll[6], voters: poll[6],
id: idx, id: idx,
timeLeft,
voteWinner: timeLeft <= 0 ? (poll[5].gt(poll[4]) ? 1 : 2) : undefined,
} }
}) })
} }
return this.lastPolls return this.lastPolls
} }
public async getVotingRoom(id: number) {
return (await this.getVotingRooms())[id]
}
public async sendVote( public async sendVote(
signer: JsonRpcSigner | Wallet, signer: JsonRpcSigner | Wallet,
roomId: number, roomId: number,

View File

@ -14,4 +14,6 @@ export type VotingRoom = {
totalVotesAgainst: BigNumber totalVotesAgainst: BigNumber
voters: string[] voters: string[]
id: number id: number
timeLeft: number
voteWinner: number | undefined
} }

View File

@ -4,35 +4,22 @@ import styled from 'styled-components'
import { Theme } from '@status-waku-voting/react-components' import { Theme } from '@status-waku-voting/react-components'
import { ProposalInfo } from './ProposalInfo' 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'
interface ProposalCardProps { interface ProposalCardProps {
id: number votingRoom: VotingRoom
theme: Theme
heading: string
text: string
address: string
mobileVersion?: boolean mobileVersion?: boolean
vote?: number theme: Theme
voteWinner?: number
hideModalFunction?: (val: boolean) => void hideModalFunction?: (val: boolean) => void
} }
export function ProposalCard({ export function ProposalCard({ theme, votingRoom, mobileVersion }: ProposalCardProps) {
id,
heading,
text,
address,
vote,
voteWinner,
theme,
mobileVersion,
}: ProposalCardProps) {
const history = useHistory() const history = useHistory()
return ( return (
<Card onClick={() => mobileVersion && history.push(`/votingRoom/${id.toString}`)}> <Card onClick={() => mobileVersion && history.push(`/votingRoom/${votingRoom.id.toString()}`)}>
<ProposalInfo heading={heading} text={text} address={address} /> <ProposalInfo votingRoom={votingRoom} />
<ProposalVote vote={vote} voteWinner={voteWinner} address={address} heading={heading} theme={theme} /> <ProposalVote votingRoom={votingRoom} theme={theme} />
</Card> </Card>
) )
} }

View File

@ -1,21 +1,20 @@
import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType'
import React from 'react' import React from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { ViewLink } from './ViewLink' import { ViewLink } from './ViewLink'
type ProposalInfoProps = { type ProposalInfoProps = {
heading: string votingRoom: VotingRoom
text: string
address: string
mobileMode?: boolean mobileMode?: boolean
} }
export function ProposalInfo({ heading, text, address, mobileMode }: ProposalInfoProps) { export function ProposalInfo({ votingRoom, mobileMode }: ProposalInfoProps) {
return ( return (
<Card> <Card>
<CardHeading>{heading}</CardHeading> <CardHeading>{votingRoom.question}</CardHeading>
<CardText className={mobileMode ? 'mobile' : ''}>{text}</CardText> <CardText className={mobileMode ? 'mobile' : ''}>{votingRoom.description}</CardText>
<CardViewLink className={mobileMode ? 'mobile' : ''}> <CardViewLink className={mobileMode ? 'mobile' : ''}>
<ViewLink address={address} /> <ViewLink address={'#'} />
</CardViewLink> </CardViewLink>
</Card> </Card>
) )

View File

@ -16,18 +16,8 @@ export function ProposalList({ theme, wakuVoting, votes }: ProposalListProps) {
const mobileVersion = useMobileVersion(ref, 600) const mobileVersion = useMobileVersion(ref, 600)
return ( return (
<List ref={ref}> <List ref={ref}>
{votes.map((vote) => { {votes.map((votingRoom) => {
return ( return <ProposalCard votingRoom={votingRoom} theme={theme} key={votingRoom.id} mobileVersion={mobileVersion} />
<ProposalCard
heading={vote.question}
text={vote.description}
address={'#'}
theme={theme}
key={vote.id}
id={vote.id}
mobileVersion={mobileVersion}
/>
)
})} })}
</List> </List>
) )

View File

@ -8,17 +8,15 @@ import { ViewLink } from '../ViewLink'
import { Modal, Theme } from '@status-waku-voting/react-components' import { Modal, Theme } from '@status-waku-voting/react-components'
import { VoteModal } from '../VoteModal' import { VoteModal } from '../VoteModal'
import { VoteAnimatedModal } from '../VoteAnimatedModal' import { VoteAnimatedModal } from '../VoteAnimatedModal'
import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType'
interface ProposalVoteProps { interface ProposalVoteProps {
theme: Theme theme: Theme
vote?: number votingRoom: VotingRoom
voteWinner?: number
heading: string
address: string
hideModalFunction?: (val: boolean) => void hideModalFunction?: (val: boolean) => void
} }
export function ProposalVote({ vote, voteWinner, address, heading, theme, hideModalFunction }: ProposalVoteProps) { export function ProposalVote({ votingRoom, theme, hideModalFunction }: ProposalVoteProps) {
const { account } = useEthers() const { account } = useEthers()
const [showVoteModal, setShowVoteModal] = useState(false) const [showVoteModal, setShowVoteModal] = useState(false)
const [showConfirmModal, setShowConfirmModal] = useState(false) const [showConfirmModal, setShowConfirmModal] = useState(false)
@ -40,11 +38,9 @@ export function ProposalVote({ vote, voteWinner, address, heading, theme, hideMo
return ( return (
<Card> <Card>
{showVoteModal && ( {showVoteModal && (
<Modal heading={heading} setShowModal={setShowVoteModal} theme={theme}> <Modal heading={votingRoom.question} setShowModal={setShowVoteModal} theme={theme}>
<VoteModal <VoteModal
votesFor={1865567} votingRoom={votingRoom}
votesAgainst={1740235}
timeLeft={4855555577}
availableAmount={65245346} availableAmount={65245346}
selectedVote={selectedVoted} selectedVote={selectedVoted}
proposingAmount={proposingAmount} proposingAmount={proposingAmount}
@ -54,29 +50,25 @@ export function ProposalVote({ vote, voteWinner, address, heading, theme, hideMo
</Modal> </Modal>
)} )}
{showConfirmModal && ( {showConfirmModal && (
<Modal heading={heading} setShowModal={hideConfirm} theme={theme}> <Modal heading={votingRoom.question} setShowModal={hideConfirm} theme={theme}>
<VoteAnimatedModal <VoteAnimatedModal
votesFor={1865567} votingRoom={votingRoom}
votesAgainst={1740235}
timeLeft={4855555577}
selectedVote={selectedVoted} selectedVote={selectedVoted}
setShowModal={hideConfirm} setShowModal={hideConfirm}
proposingAmount={proposingAmount} proposingAmount={proposingAmount}
/> />
</Modal> </Modal>
)} )}
{voteWinner ? <CardHeading>Proposal {voteWinner == 1 ? 'rejected' : 'passed'}</CardHeading> : <CardHeading />} {votingRoom.voteWinner ? (
<CardHeading>Proposal {votingRoom.voteWinner == 1 ? 'rejected' : 'passed'}</CardHeading>
) : (
<CardHeading />
)}
<VoteChart <VoteChart votingRoom={votingRoom} selectedVote={selectedVoted} />
votesFor={1865567}
votesAgainst={1740235}
timeLeft={4855555577}
voteWinner={voteWinner}
selectedVote={selectedVoted}
/>
<CardButtons> <CardButtons>
{voteWinner ? ( {votingRoom.voteWinner ? (
<FinalBtn disabled={!account}>Finalize the vote</FinalBtn> <FinalBtn disabled={!account}>Finalize the vote</FinalBtn>
) : ( ) : (
<VotesBtns> <VotesBtns>
@ -105,9 +97,9 @@ export function ProposalVote({ vote, voteWinner, address, heading, theme, hideMo
<CardVoteBottom> <CardVoteBottom>
<CardViewLink> <CardViewLink>
{' '} {' '}
<ViewLink address={address} /> <ViewLink address={'#'} />
</CardViewLink> </CardViewLink>
{vote && <VoteSubmitButton votes={vote} disabled={!account} />} <VoteSubmitButton votes={15} disabled={!account} />
</CardVoteBottom> </CardVoteBottom>
</Card> </Card>
) )

View File

@ -1,4 +1,4 @@
import React, { useRef } from 'react' import React, { useMemo, useRef } from 'react'
import CountUp from 'react-countup' import CountUp from 'react-countup'
import styled from 'styled-components' import styled from 'styled-components'
import { addCommas } from '../../helpers/addCommas' import { addCommas } from '../../helpers/addCommas'
@ -9,43 +9,39 @@ import crossWinnerIcon from '../../assets/svg/crossWinner.svg'
import checkIcon from '../../assets/svg/check.svg' import checkIcon from '../../assets/svg/check.svg'
import checkWinnerIcon from '../../assets/svg/checkWinner.svg' import checkWinnerIcon from '../../assets/svg/checkWinner.svg'
import { useMobileVersion } from '@status-waku-voting/react-components' import { useMobileVersion } from '@status-waku-voting/react-components'
import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType'
export interface VoteChartProps { export interface VoteChartProps {
votesFor: number votingRoom: VotingRoom
votesAgainst: number
timeLeft: number
voteWinner?: number
proposingAmount?: number proposingAmount?: number
selectedVote?: number selectedVote?: number
isAnimation?: boolean isAnimation?: boolean
tabletMode?: (val: boolean) => void tabletMode?: (val: boolean) => void
} }
export function VoteChart({ export function VoteChart({ votingRoom, proposingAmount, selectedVote, isAnimation, tabletMode }: VoteChartProps) {
votesFor,
votesAgainst,
timeLeft,
voteWinner,
proposingAmount,
selectedVote,
isAnimation,
tabletMode,
}: VoteChartProps) {
const ref = useRef<HTMLHeadingElement>(null) const ref = useRef<HTMLHeadingElement>(null)
const mobileVersion = useMobileVersion(ref, 600) const mobileVersion = useMobileVersion(ref, 600)
const voteSum = votesFor + votesAgainst const voteSum = useMemo(
const graphWidth = (100 * votesAgainst) / voteSum () => votingRoom.totalVotesFor.add(votingRoom.totalVotesAgainst),
[votingRoom.totalVotesFor.toString(), votingRoom.totalVotesAgainst.toString()]
)
const graphWidth = useMemo(() => votingRoom.totalVotesAgainst.mul(100).div(voteSum).toNumber(), [voteSum])
let balanceWidth = graphWidth const balanceWidth = useMemo(() => {
if (!proposingAmount) {
if (proposingAmount) { return graphWidth
balanceWidth = } else {
selectedVote === 0 const divider = voteSum.add(proposingAmount)
? (100 * (votesAgainst + proposingAmount)) / (voteSum + proposingAmount) return selectedVote === 0
: (100 * votesAgainst) / (voteSum + proposingAmount) ? votingRoom.totalVotesAgainst.add(proposingAmount).mul(100).div(divider).toNumber()
} : votingRoom.totalVotesAgainst.mul(100).div(divider).toNumber()
}
}, [graphWidth, voteSum, proposingAmount])
const timeLeft = useMemo(() => votingRoom.timeLeft, [votingRoom.timeLeft])
const voteWinner = useMemo(() => votingRoom.voteWinner, [votingRoom.voteWinner])
return ( return (
<Votes ref={ref}> <Votes ref={ref}>
<VotesChart className={selectedVote || tabletMode ? '' : 'notModal'}> <VotesChart className={selectedVote || tabletMode ? '' : 'notModal'}>
@ -65,9 +61,9 @@ export function VoteChart({
<span> <span>
{' '} {' '}
{isAnimation && proposingAmount && selectedVote && selectedVote === 0 ? ( {isAnimation && proposingAmount && selectedVote && selectedVote === 0 ? (
<CountUp end={votesAgainst + proposingAmount} separator="," /> <CountUp end={votingRoom.totalVotesAgainst.toNumber() + proposingAmount} separator="," />
) : ( ) : (
addCommas(votesAgainst) addCommas(votingRoom.totalVotesAgainst.toNumber())
)}{' '} )}{' '}
<span style={{ fontWeight: 'normal' }}>ABC</span> <span style={{ fontWeight: 'normal' }}>ABC</span>
</span> </span>
@ -89,9 +85,9 @@ export function VoteChart({
<span> <span>
{' '} {' '}
{isAnimation && proposingAmount && selectedVote && selectedVote === 1 ? ( {isAnimation && proposingAmount && selectedVote && selectedVote === 1 ? (
<CountUp end={votesFor + proposingAmount} separator="," /> <CountUp end={votingRoom.totalVotesFor.toNumber() + proposingAmount} separator="," />
) : ( ) : (
addCommas(votesFor) addCommas(votingRoom.totalVotesFor.toNumber())
)}{' '} )}{' '}
<span style={{ fontWeight: 'normal' }}>ABC</span> <span style={{ fontWeight: 'normal' }}>ABC</span>
</span> </span>

View File

@ -1,32 +1,22 @@
import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType'
import React from 'react' import React from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { FinalBtn } from './Buttons' import { FinalBtn } from './Buttons'
import { VoteChart } from './ProposalVoteCard/VoteChart' import { VoteChart } from './ProposalVoteCard/VoteChart'
interface VoteAnimatedModalProps { interface VoteAnimatedModalProps {
votesFor: number votingRoom: VotingRoom
votesAgainst: number
timeLeft: number
proposingAmount: number proposingAmount: number
selectedVote: number selectedVote: number
setShowModal: (val: boolean) => void setShowModal: (val: boolean) => void
} }
export function VoteAnimatedModal({ export function VoteAnimatedModal({ votingRoom, selectedVote, proposingAmount, setShowModal }: VoteAnimatedModalProps) {
votesFor,
votesAgainst,
timeLeft,
selectedVote,
proposingAmount,
setShowModal,
}: VoteAnimatedModalProps) {
return ( return (
<VoteConfirm> <VoteConfirm>
<ConfirmText>Your vote {selectedVote === 0 ? 'against' : 'for'} this proposal has been cast!</ConfirmText> <ConfirmText>Your vote {selectedVote === 0 ? 'against' : 'for'} this proposal has been cast!</ConfirmText>
<VoteChart <VoteChart
votesFor={votesFor} votingRoom={votingRoom}
votesAgainst={votesAgainst}
timeLeft={timeLeft}
proposingAmount={proposingAmount} proposingAmount={proposingAmount}
selectedVote={selectedVote} selectedVote={selectedVote}
isAnimation={true} isAnimation={true}

View File

@ -3,24 +3,19 @@ import styled from 'styled-components'
import { VoteChart } from './ProposalVoteCard/VoteChart' import { VoteChart } from './ProposalVoteCard/VoteChart'
import { DisabledButton, VoteBtnAgainst, VoteBtnFor } from './Buttons' import { DisabledButton, VoteBtnAgainst, VoteBtnFor } from './Buttons'
import { VotePropose } from './VotePropose' import { VotePropose } from './VotePropose'
import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType'
export interface VoteModalProps { export interface VoteModalProps {
votesFor: number votingRoom: VotingRoom
votesAgainst: number
timeLeft: number
voteWinner?: number
selectedVote: number
availableAmount: number availableAmount: number
selectedVote: number
proposingAmount: number proposingAmount: number
setShowConfirmModal: (show: boolean) => void setShowConfirmModal: (show: boolean) => void
setProposingAmount: (val: number) => void setProposingAmount: (val: number) => void
} }
export function VoteModal({ export function VoteModal({
votesFor, votingRoom,
votesAgainst,
timeLeft,
voteWinner,
selectedVote, selectedVote,
availableAmount, availableAmount,
proposingAmount, proposingAmount,
@ -32,14 +27,7 @@ export function VoteModal({
return ( return (
<Column> <Column>
<VoteChart <VoteChart votingRoom={votingRoom} proposingAmount={proposingAmount} selectedVote={selectedVote} />
votesFor={votesFor}
votesAgainst={votesAgainst}
timeLeft={timeLeft}
voteWinner={voteWinner}
proposingAmount={proposingAmount}
selectedVote={selectedVote}
/>
<VotePropose <VotePropose
availableAmount={availableAmount} availableAmount={availableAmount}
setProposingAmount={setProposingAmount} setProposingAmount={setProposingAmount}

View File

@ -17,7 +17,9 @@ export function ProposalMobile({ wakuVoting }: ProposalMobileProps) {
<ProposalWrapper> <ProposalWrapper>
<Switch> <Switch>
<Route exact path="/" render={() => <Redirect to="/proposal" />} /> <Route exact path="/" render={() => <Redirect to="/proposal" />} />
<Route exact path="/votingRoom/:id" component={ProposalVoteMobile} /> <Route exact path="/votingRoom/:id">
<ProposalVoteMobile wakuVoting={wakuVoting} availableAmount={123} />
</Route>
<Route exact path="/creation" component={ProposeMobile} /> <Route exact path="/creation" component={ProposeMobile} />
<Route exact path="/proposal"> <Route exact path="/proposal">
<ProposalMainMobile wakuVoting={wakuVoting} /> <ProposalMainMobile wakuVoting={wakuVoting} />

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react' import React, { useEffect, useMemo, useState } from 'react'
import { useParams } from 'react-router' import { useParams } from 'react-router'
import styled from 'styled-components' import styled from 'styled-components'
import { useEthers } from '@usedapp/core' import { useEthers } from '@usedapp/core'
@ -8,48 +8,31 @@ 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 { useVotingRoom } from '@status-waku-voting/proposal-hooks'
import { WakuVoting } from '@status-waku-voting/core'
interface ProposalVoteMobileProps { interface ProposalVoteMobileProps {
vote?: number wakuVoting: WakuVoting
voteWinner?: number
votesFor: number
votesAgainst: number
timeLeft: number
availableAmount: number availableAmount: number
heading: string
text: string
address: string
} }
export function ProposalVoteMobile({ export function ProposalVoteMobile({ wakuVoting, availableAmount }: ProposalVoteMobileProps) {
votesFor,
votesAgainst,
timeLeft,
vote,
voteWinner,
address,
heading,
text,
availableAmount,
}: ProposalVoteMobileProps) {
const { id } = useParams<{ id: string }>() const { id } = useParams<{ id: string }>()
const { account } = useEthers() const { account } = useEthers()
const [proposingAmount, setProposingAmount] = useState(0) const [proposingAmount, setProposingAmount] = useState(0)
const [selectedVoted, setSelectedVoted] = useState(0) const [selectedVoted, setSelectedVoted] = useState(0)
const [mobileVersion, setMobileVersion] = useState(true) const votingRoom = useVotingRoom(Number(id), wakuVoting)
const voteWinner = useMemo(() => votingRoom?.voteWinner, [votingRoom?.voteWinner])
if (!votingRoom) {
return <>Loading</>
}
return ( return (
<Card> <Card>
<ProposalInfo <ProposalInfo votingRoom={votingRoom} mobileMode={true} />
heading={'This is a very long, explainative and sophisticated title for a proposal.'}
text={
'This is a longer description of the proposal. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque interdum rutrum sodales. Nullam mattis fermentum libero, non volutpat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque interdum rutrum sodales. Nullam mattis fermentum libero. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque interdum rutrum sodales. Nullam mattis fermentum libero.'
}
address={'#'}
mobileMode={mobileVersion}
/>
<VoteChartWrap> <VoteChartWrap>
<VoteChart votesFor={1865567} votesAgainst={1740235} timeLeft={4855555577} selectedVote={selectedVoted} /> <VoteChart votingRoom={votingRoom} selectedVote={selectedVoted} />
</VoteChartWrap> </VoteChartWrap>
{!voteWinner && ( {!voteWinner && (
<VotePropose <VotePropose
@ -86,7 +69,7 @@ export function ProposalVoteMobile({
<CardVoteBottom> <CardVoteBottom>
{' '} {' '}
<VoteSubmitButton votes={2345678} disabled={!account} /> <VoteSubmitButton votes={15} disabled={!account} />
</CardVoteBottom> </CardVoteBottom>
</Card> </Card>
) )

View File

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

View File

@ -1,3 +1,3 @@
import { useWakuProposal } from './hooks/useWakuProposal' import { useWakuProposal } from './hooks/useWakuProposal'
import { useVotingRoom } from './hooks/useVotingRoom'
export { useWakuProposal } export { useWakuProposal, useVotingRoom }