Refactor proposal modal and mobile (#75)

This commit is contained in:
Szymon Szlachtowicz 2021-09-16 14:34:14 +02:00 committed by GitHub
parent e70c0801d7
commit 2b49035373
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 121 additions and 250 deletions

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import styled from 'styled-components'
import { ProposalHeader } from './ProposalHeader'
import { blueTheme } from '@status-waku-voting/react-components/dist/esm/src/style/themes'
@ -8,6 +8,11 @@ import { WakuVoting } from '@status-waku-voting/core'
import { VotingEmpty } from './VotingEmpty'
import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType'
import { useTokenBalance } from '@status-waku-voting/react-components'
import { NewVoteModal } from './newVoteModal/NewVoteModal'
import { useEthers } from '@usedapp/core'
import { Modal, Networks, useMobileVersion } from '@status-waku-voting/react-components'
import { useHistory } from 'react-router'
import { useVotingRooms } from '@status-waku-voting/proposal-hooks'
type ProposalProps = {
wakuVoting: WakuVoting
@ -15,24 +20,56 @@ type ProposalProps = {
}
export function Proposal({ wakuVoting, account }: ProposalProps) {
const [votes, setVotes] = useState<VotingRoom[]>([])
const votes = useVotingRooms(wakuVoting)
const tokenBalance = useTokenBalance(account, wakuVoting)
const [showNewVoteModal, setShowNewVoteModal] = useState(false)
const [showConnectionModal, setShowConnectionModal] = useState(false)
useEffect(() => {
const interval = setInterval(async () => {
setVotes(await wakuVoting.getVotingRooms())
}, 10000)
wakuVoting.getVotingRooms().then((e) => setVotes(e))
return () => clearInterval(interval)
const { activateBrowserWallet } = useEthers()
const history = useHistory()
const ref = useRef<HTMLHeadingElement>(null)
const mobileVersion = useMobileVersion(ref, 600)
const onCreateClick = useCallback(() => {
mobileVersion ? history.push(`/creation`) : setShowNewVoteModal(true)
}, [mobileVersion])
const onConnectClick = useCallback(() => {
if ((window as any).ethereum) {
activateBrowserWallet()
} else setShowConnectionModal(true)
}, [])
return (
<ProposalWrapper>
<ProposalWrapper ref={ref}>
<NewVoteModal
theme={blueTheme}
availableAmount={tokenBalance}
setShowModal={setShowNewVoteModal}
showModal={showNewVoteModal}
wakuVoting={wakuVoting}
/>
{showConnectionModal && (
<Modal heading="Connect" setShowModal={setShowConnectionModal} theme={blueTheme}>
<Networks />
</Modal>
)}
{votes && votes?.length === 0 ? (
<VotingEmpty wakuVoting={wakuVoting} theme={blueTheme} availableAmount={tokenBalance} />
<VotingEmpty
account={account}
theme={blueTheme}
onConnectClick={onConnectClick}
onCreateClick={onCreateClick}
/>
) : (
<ProposalVotesWrapper>
<ProposalHeader theme={blueTheme} wakuVoting={wakuVoting} availableAmount={tokenBalance} />
<ProposalHeader
account={account}
theme={blueTheme}
onConnectClick={onConnectClick}
onCreateClick={onCreateClick}
/>
<ProposalList theme={blueTheme} wakuVoting={wakuVoting} votes={votes} availableAmount={tokenBalance} />
</ProposalVotesWrapper>
)}
@ -51,7 +88,16 @@ const ProposalWrapper = styled.div`
padding: 150px 32px 50px;
width: 100%;
min-height: 100vh;
@media (max-width: 600px) {
padding: 132px 16px 32px;
}
@media (max-width: 425px) {
padding: 64px 16px 84px;
}
`
export const ProposalVotesWrapper = styled(ProposalWrapper)`
width: 100%;
padding: 0;

View File

@ -3,63 +3,32 @@ import styled from 'styled-components'
import { useEthers } from '@usedapp/core'
import { Modal, Networks, CreateButton } from '@status-waku-voting/react-components'
import { Theme } from '@status-waku-voting/react-components/dist/esm/src/style/themes'
import { WakuVoting } from '@status-waku-voting/core'
import { BigNumber } from 'ethers'
import { NewVoteModal } from './newVoteModal/NewVoteModal'
type ProposalHeaderProps = {
theme: Theme
wakuVoting: WakuVoting
availableAmount: number
account: string | null | undefined
onCreateClick: () => void
onConnectClick: () => void
}
export function ProposalHeader({ theme, wakuVoting, availableAmount }: ProposalHeaderProps) {
const { activateBrowserWallet, account, library } = useEthers()
const [selectConnect, setSelectConnect] = useState(false)
const [showModal, setShowModal] = useState(false)
export function ProposalHeader({ theme, account, onCreateClick, onConnectClick }: ProposalHeaderProps) {
return (
<Wrapper>
<NewVoteModal
theme={theme}
availableAmount={availableAmount}
setShowModal={setShowModal}
showModal={showModal}
wakuVoting={wakuVoting}
/>
<Header>
<Heading>Your voice has real power</Heading>
<HeaderText>
Take part in a decentralised governance by voting on proposals provided by community or creating your own.
</HeaderText>
</Header>
{account ? (
<CreateButton
theme={theme}
onClick={() => {
setShowModal(true)
}}
>
<CreateButton theme={theme} onClick={onCreateClick}>
Create proposal
</CreateButton>
) : (
<CreateButton
theme={theme}
onClick={() => {
if ((window as any).ethereum) {
activateBrowserWallet()
} else setSelectConnect(true)
}}
>
<CreateButton theme={theme} onClick={onConnectClick}>
Connect to vote
</CreateButton>
)}
{selectConnect && (
<Modal heading="Connect" setShowModal={setSelectConnect} theme={theme}>
<Networks />
</Modal>
)}
</Wrapper>
)
}
@ -81,6 +50,7 @@ const Header = styled.div`
padding: 12px 16px 0;
width: 100%;
background: #f8faff;
z-index: 10;
}
`

View File

@ -1,35 +1,17 @@
import React, { useEffect, useState, useRef } from 'react'
import { useHistory } from 'react-router'
import { useEthers } from '@usedapp/core'
import React from 'react'
import styled from 'styled-components'
import { CreateButton, Modal, Networks, Theme, useMobileVersion } from '@status-waku-voting/react-components'
import { WakuVoting } from '@status-waku-voting/core'
import { NewVoteModal } from './newVoteModal/NewVoteModal'
import { CreateButton, Theme } from '@status-waku-voting/react-components'
type VotingEmptyProps = {
theme: Theme
wakuVoting: WakuVoting
availableAmount: number
account: string | null | undefined
onCreateClick: () => void
onConnectClick: () => void
}
export function VotingEmpty({ wakuVoting, theme, availableAmount }: VotingEmptyProps) {
const { account, activateBrowserWallet } = useEthers()
const [selectConnect, setSelectConnect] = useState(false)
const [showModal, setShowModal] = useState(false)
const history = useHistory()
const ref = useRef<HTMLHeadingElement>(null)
const mobileVersion = useMobileVersion(ref, 600)
export function VotingEmpty({ theme, account, onCreateClick, onConnectClick }: VotingEmptyProps) {
return (
<VotingEmptyWrap ref={ref}>
<NewVoteModal
theme={theme}
availableAmount={availableAmount}
setShowModal={setShowModal}
showModal={showModal}
wakuVoting={wakuVoting}
/>
<VotingEmptyWrap>
<EmptyWrap>
<EmptyHeading>There are no proposals at the moment!</EmptyHeading>
<EmptyText>
@ -38,32 +20,14 @@ export function VotingEmpty({ wakuVoting, theme, availableAmount }: VotingEmptyP
</EmptyText>
</EmptyWrap>
{account ? (
<EmptyCreateButton
theme={theme}
onClick={() => {
mobileVersion ? history.push(`/creation`) : setShowModal(true)
}}
>
<EmptyCreateButton theme={theme} onClick={onCreateClick}>
Create proposal
</EmptyCreateButton>
) : (
<CreateButton
theme={theme}
onClick={() => {
if ((window as any).ethereum) {
activateBrowserWallet()
} else setSelectConnect(true)
}}
>
<CreateButton theme={theme} onClick={onConnectClick}>
Connect to vote
</CreateButton>
)}
{selectConnect && (
<Modal heading="Connect" setShowModal={setSelectConnect} theme={theme}>
<Networks />
</Modal>
)}
</VotingEmptyWrap>
)
}

View File

@ -1,92 +0,0 @@
import React, { useState } from 'react'
import { useHistory } from 'react-router'
import styled from 'styled-components'
import { useEthers } from '@usedapp/core'
import { Modal, Networks, CreateButton } from '@status-waku-voting/react-components'
import { Theme } from '@status-waku-voting/react-components/dist/esm/src/style/themes'
import { Wrapper } from '../ProposalHeader'
type ProposalHeaderMobileProps = {
theme: Theme
}
export function ProposalHeaderMobile({ theme }: ProposalHeaderMobileProps) {
const { activateBrowserWallet, account } = useEthers()
const [selectConnect, setSelectConnect] = useState(false)
const history = useHistory()
return (
<Wrapper>
<Header>
<Heading>Your voice has real power</Heading>
<HeaderText>
Take part in a decentralised governance by voting on proposals provided by community or creating your own.
</HeaderText>
</Header>
{account ? (
<CreateButton theme={theme} onClick={() => history.push(`/creation`)}>
Create proposal
</CreateButton>
) : (
<CreateButton
theme={theme}
onClick={() => {
if ((window as any).ethereum) {
activateBrowserWallet()
} else setSelectConnect(true)
}}
>
Connect to vote
</CreateButton>
)}
{selectConnect && (
<Modal heading="Connect" setShowModal={setSelectConnect} theme={theme}>
<Networks />
</Modal>
)}
</Wrapper>
)
}
const Header = styled.div`
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
max-width: 680px;
@media (max-width: 425px) {
position: fixed;
padding: 12px 16px 0;
width: 100%;
background: #f8faff;
z-index: 10;
}
`
const Heading = styled.h1`
font-weight: bold;
font-size: 28px;
line-height: 38px;
letter-spacing: -0.4px;
margin: 0;
margin-bottom: 8px;
@media (max-width: 425px) {
font-size: 22px;
line-height: 30px;
}
`
const HeaderText = styled.p`
font-size: 22px;
line-height: 32px;
margin: 0;
margin-bottom: 24px;
@media (max-width: 425px) {
font-size: 13px;
line-height: 18px;
margin-bottom: 16px;
}
`

View File

@ -1,55 +0,0 @@
import React, { useEffect, useState } from 'react'
import { blueTheme } from '@status-waku-voting/react-components/dist/esm/src/style/themes'
import { ProposalList } from '../ProposalList'
import { VotingEmpty } from '../VotingEmpty'
import { NotificationItem } from '../NotificationItem'
import { ProposalHeaderMobile } from './ProposalHeaderMobile'
import styled from 'styled-components'
import { WakuVoting } from '@status-waku-voting/core'
import { ProposalVotesWrapper } from '../Proposal'
import { VotingRoom } from '@status-waku-voting/core/dist/esm/src/types/PollType'
type ProposalMainMobileProps = {
wakuVoting: WakuVoting
availableAmount: number
}
export function ProposalMainMobile({ wakuVoting, availableAmount }: ProposalMainMobileProps) {
const [votes, setVotes] = useState<VotingRoom[]>([])
useEffect(() => {
const interval = setInterval(async () => {
setVotes(await wakuVoting.getVotingRooms())
}, 10000)
return () => clearInterval(interval)
}, [])
return (
<ProposalWrapper>
{votes && votes?.length === 0 ? (
<VotingEmpty wakuVoting={wakuVoting} theme={blueTheme} availableAmount={availableAmount} />
) : (
<ProposalVotesWrapper>
<ProposalHeaderMobile theme={blueTheme} />
<ProposalList theme={blueTheme} wakuVoting={wakuVoting} votes={votes} availableAmount={availableAmount} />
</ProposalVotesWrapper>
)}
<NotificationItem text={'Proposal you finalized will be settled after 10 confirmations.'} address={'#'} />
</ProposalWrapper>
)
}
const ProposalWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
max-width: 1000px;
margin: 0 auto;
width: 100%;
min-height: 100vh;
padding: 132px 16px 32px;
@media (max-width: 425px) {
padding: 64px 16px 84px;
}
`

View File

@ -4,7 +4,7 @@ import { BrowserRouter } from 'react-router-dom'
import styled from 'styled-components'
import { ProposalVoteMobile } from './ProposalVoteMobile'
import { ProposeMobile } from './ProposeMobile'
import { ProposalMainMobile } from './ProposalMainMobile'
import { Proposal } from '../Proposal'
import { WakuVoting } from '@status-waku-voting/core'
import { useTokenBalance } from '@status-waku-voting/react-components'
@ -27,7 +27,7 @@ export function ProposalMobile({ wakuVoting, account }: ProposalMobileProps) {
<ProposeMobile availableAmount={tokenBalance} />
</Route>
<Route exact path="/proposal">
<ProposalMainMobile wakuVoting={wakuVoting} availableAmount={tokenBalance} />
<Proposal wakuVoting={wakuVoting} account={account} />
</Route>
</Switch>
</ProposalWrapper>

View File

@ -1,6 +1,6 @@
import { WakuVoting } from '@status-waku-voting/core'
import { Modal, Theme } from '@status-waku-voting/react-components'
import React, { useState } from 'react'
import React, { useCallback, useEffect, useState } from 'react'
import { ProposeModal } from './ProposeModal'
import { ProposeVoteModal } from './ProposeVoteModal'
@ -17,6 +17,14 @@ export function NewVoteModal({ theme, showModal, setShowModal, availableAmount,
const [title, setTitle] = useState('')
const [text, setText] = useState('')
useEffect(() => {
if (!showModal) {
setScreen(1)
setTitle('')
setText('')
}
}, [showModal])
if (!showModal) {
return null
}

View File

@ -26,7 +26,7 @@ export function ProposeModal({
return (
<ProposingData>
{insufficientFunds && (
{availableAmount < 10000 && (
<ProposingInfo>
<span></span>
<InfoText>You need at least 10,000 ABC to create a proposal!</InfoText>

View File

@ -27,7 +27,6 @@ export function ProposeVoteModal({
setTitle,
setText,
}: ProposeVoteModalProps) {
const { library } = useEthers()
const [proposingAmount, setProposingAmount] = useState(0)
return (
<ProposingData>

View File

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

View File

@ -1,5 +1,5 @@
import { WakuVoting } from '@status-waku-voting/core'
import React, { useEffect, useState } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import { Web3Provider } from '@ethersproject/providers'
export function useWakuProposal(
@ -9,16 +9,21 @@ export function useWakuProposal(
multicallAddress: string | undefined
) {
const [waku, setWaku] = useState<WakuVoting | undefined>(undefined)
const queuePos = useRef(0)
const queueSize = useRef(0)
useEffect(() => {
;(window as any).ethereum.on('chainChanged', () => window.location.reload())
const createWaku = async () => {
const createWaku = async (queue: number) => {
while (queue != queuePos.current) {
await new Promise((r) => setTimeout(r, 1000))
}
if (provider && multicallAddress) {
const wak = await WakuVoting.create(appName, contractAddress, provider, multicallAddress)
setWaku(wak)
}
queuePos.current++
}
createWaku()
createWaku(queueSize.current++)
return () => (window as any).ethereum.removeListener('chainChanged', () => window.location.reload())
}, [provider, multicallAddress, contractAddress])

View File

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