Refactor proposal modal and mobile (#75)
This commit is contained in:
parent
e70c0801d7
commit
2b49035373
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
`
|
||||
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -27,7 +27,6 @@ export function ProposeVoteModal({
|
|||
setTitle,
|
||||
setText,
|
||||
}: ProposeVoteModalProps) {
|
||||
const { library } = useEthers()
|
||||
const [proposingAmount, setProposingAmount] = useState(0)
|
||||
return (
|
||||
<ProposingData>
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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])
|
||||
|
|
|
@ -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 }
|
||||
|
|
Loading…
Reference in New Issue