Add propose mobile (#143)
This commit is contained in:
parent
dc12c4e27b
commit
010b7f56d0
|
@ -0,0 +1,36 @@
|
|||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { Input } from './Input'
|
||||
|
||||
interface PublicKeyInputProps {
|
||||
publicKey: string
|
||||
setPublicKey: (val: string) => void
|
||||
}
|
||||
|
||||
export function PublicKeyInput({ publicKey, setPublicKey }: PublicKeyInputProps) {
|
||||
return (
|
||||
<CommunityKeyLabel>
|
||||
Community public key
|
||||
<CommunityKey
|
||||
value={publicKey}
|
||||
placeholder="E.g. 0xbede83eef5d82c4dd5d82c4dd5fa837ad"
|
||||
onChange={(e) => {
|
||||
setPublicKey(e.currentTarget.value)
|
||||
}}
|
||||
></CommunityKey>
|
||||
</CommunityKeyLabel>
|
||||
)
|
||||
}
|
||||
|
||||
const CommunityKey = styled(Input)`
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 32px;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
`
|
||||
const CommunityKeyLabel = styled.label`
|
||||
width: 100%;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
`
|
|
@ -2,37 +2,18 @@ import React, { useEffect, useState } from 'react'
|
|||
import styled from 'styled-components'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
import { CardCommunity } from './CardCommunity'
|
||||
import { Input } from '../Input'
|
||||
import { VotePropose } from '../votes/VotePropose'
|
||||
import { Warning } from '../votes/VoteWarning'
|
||||
import { ConfirmBtn } from './VoteConfirmModal'
|
||||
import { useContractCall, useContractFunction } from '@usedapp/core'
|
||||
import { useContractFunction } from '@usedapp/core'
|
||||
import { useContracts } from '../../hooks/useContracts'
|
||||
import { CommunityDetail } from '../../models/community'
|
||||
import { CommunitySkeleton } from '../skeleton/CommunitySkeleton'
|
||||
import { useCommunityDetails } from '../../hooks/useCommunityDetails'
|
||||
import { ColumnFlexDiv } from '../../constants/styles'
|
||||
import { BigNumber } from 'ethers'
|
||||
|
||||
interface PublicKeyInputProps {
|
||||
publicKey: string
|
||||
setPublicKey: (val: string) => void
|
||||
}
|
||||
|
||||
function PublicKeyInput({ publicKey, setPublicKey }: PublicKeyInputProps) {
|
||||
return (
|
||||
<CommunityKeyLabel>
|
||||
Community public key
|
||||
<CommunityKey
|
||||
value={publicKey}
|
||||
placeholder="E.g. 0xbede83eef5d82c4dd5d82c4dd5fa837ad"
|
||||
onChange={(e) => {
|
||||
setPublicKey(e.currentTarget.value)
|
||||
}}
|
||||
></CommunityKey>
|
||||
</CommunityKeyLabel>
|
||||
)
|
||||
}
|
||||
import { useProposeWarning } from '../../hooks/useProposeWarning'
|
||||
import { PublicKeyInput } from '../PublicKeyInput'
|
||||
|
||||
interface ProposeModalProps {
|
||||
availableAmount: number
|
||||
|
@ -49,25 +30,11 @@ export function ProposeModal({
|
|||
}: ProposeModalProps) {
|
||||
const [proposingAmount, setProposingAmount] = useState(0)
|
||||
const [publicKey, setPublicKey] = useState('')
|
||||
const [communityInDirectory, setCommunityInDirectory] = useState(false)
|
||||
const [communityUnderVote, setCommunityUnderVote] = useState(false)
|
||||
const loading = useCommunityDetails(publicKey, setCommunityFound)
|
||||
const { votingContract, directoryContract } = useContracts()
|
||||
const { votingContract } = useContracts()
|
||||
const { send, state } = useContractFunction(votingContract, 'initializeVotingRoom')
|
||||
|
||||
const [isCommunityInDirectory] = useContractCall({
|
||||
abi: directoryContract.interface,
|
||||
address: directoryContract.address,
|
||||
method: 'isCommunityInDirectory',
|
||||
args: [publicKey],
|
||||
}) ?? [undefined]
|
||||
|
||||
const [isCommunityUnderVote] = useContractCall({
|
||||
abi: votingContract.interface,
|
||||
address: votingContract.address,
|
||||
method: 'communityVotingId',
|
||||
args: [publicKey],
|
||||
}) ?? [undefined]
|
||||
const warning = useProposeWarning(communityFound, availableAmount)
|
||||
|
||||
useEffect(() => {
|
||||
if (state.status === 'Mining') {
|
||||
|
@ -75,62 +42,12 @@ export function ProposeModal({
|
|||
}
|
||||
}, [state])
|
||||
|
||||
useEffect(() => {
|
||||
if (isCommunityInDirectory) {
|
||||
setCommunityInDirectory(true)
|
||||
} else {
|
||||
setCommunityInDirectory(false)
|
||||
}
|
||||
}, [isCommunityInDirectory])
|
||||
|
||||
useEffect(() => {
|
||||
if (isCommunityUnderVote && isCommunityUnderVote.toNumber() > 0) {
|
||||
setCommunityUnderVote(true)
|
||||
} else {
|
||||
setCommunityUnderVote(false)
|
||||
}
|
||||
}, [isCommunityUnderVote?.toNumber()])
|
||||
|
||||
return (
|
||||
<ColumnFlexDiv>
|
||||
<PublicKeyInput publicKey={publicKey} setPublicKey={setPublicKey} />
|
||||
<ProposingData>
|
||||
{communityFound ? <CardCommunity community={communityFound} /> : loading && publicKey && <CommunitySkeleton />}
|
||||
{communityFound && (
|
||||
<WarningWrap>
|
||||
{communityFound.numberOfMembers < 42 && (
|
||||
<Warning
|
||||
icon="🤏"
|
||||
text={`${communityFound.name} currently only has ${communityFound.numberOfMembers} members. A community needs more than 42 members before a vote to be added to the Status community directory can be proposed.`}
|
||||
/>
|
||||
)}
|
||||
{availableAmount < 10000 && (
|
||||
<Warning
|
||||
icon="💰"
|
||||
text={`Not enough SNT to start a vote for this community. A new vote for ${communityFound.name} requires at least 10,000 SNT available.`}
|
||||
/>
|
||||
)}
|
||||
{!communityFound.ens && (
|
||||
<Warning
|
||||
icon="⚠️"
|
||||
text={`${communityFound.name} is not registered in Ethereum Name Service. Only communities with ENS name can be included in the directory.`}
|
||||
/>
|
||||
)}
|
||||
{communityInDirectory && (
|
||||
<Warning
|
||||
icon="⚠️"
|
||||
text={`${communityFound.name} is already in the communities directory! No need to start a new vote.`}
|
||||
/>
|
||||
)}
|
||||
{communityUnderVote && (
|
||||
<Warning
|
||||
icon="⚠️"
|
||||
text={`There’s already an ongoing vote to add ${communityFound.name} in the directory!`}
|
||||
/>
|
||||
)}
|
||||
</WarningWrap>
|
||||
)}
|
||||
|
||||
<WarningWrap>{warning.text && <Warning icon={warning.icon} text={warning.text} />}</WarningWrap>
|
||||
{communityFound && communityFound.validForAddition && publicKey && (
|
||||
<VoteProposeWrap>
|
||||
<VotePropose
|
||||
|
@ -155,7 +72,7 @@ export function ProposeModal({
|
|||
</ConfirmBtn>
|
||||
) : (
|
||||
<ProposingBtn
|
||||
disabled={!communityFound || !proposingAmount}
|
||||
disabled={!communityFound || !proposingAmount || !!warning.text}
|
||||
onClick={() => send(1, publicKey, BigNumber.from(proposingAmount))}
|
||||
>
|
||||
Confirm vote to add community
|
||||
|
@ -165,19 +82,6 @@ export function ProposeModal({
|
|||
)
|
||||
}
|
||||
|
||||
const CommunityKey = styled(Input)`
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 32px;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
`
|
||||
const CommunityKeyLabel = styled.label`
|
||||
width: 100%;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
`
|
||||
|
||||
const VoteProposeWrap = styled.div`
|
||||
margin-top: 32px;
|
||||
`
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
import { useContractFunction } from '@usedapp/core'
|
||||
import React, { useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { CardCommunity } from '../components/card/CardCommunity'
|
||||
import { PublicKeyInput } from '../components/PublicKeyInput'
|
||||
import { CommunitySkeleton } from '../components/skeleton/CommunitySkeleton'
|
||||
import { VotePropose } from '../components/votes/VotePropose'
|
||||
import { Warning } from '../components/votes/VoteWarning'
|
||||
import { ColumnFlexDiv } from '../constants/styles'
|
||||
import { useCommunityDetails } from '../hooks/useCommunityDetails'
|
||||
import { useContracts } from '../hooks/useContracts'
|
||||
import { useProposeWarning } from '../hooks/useProposeWarning'
|
||||
import { CommunityDetail } from '../models/community'
|
||||
import { BigNumber } from 'ethers'
|
||||
import { ButtonPrimary } from '../components/Button'
|
||||
|
||||
export function ProposeMobile() {
|
||||
const availableAmount = 60000000
|
||||
const [proposingAmount, setProposingAmount] = useState(0)
|
||||
const [communityFound, setCommunityFound] = useState<CommunityDetail | undefined>(undefined)
|
||||
const [publicKey, setPublicKey] = useState('')
|
||||
const loading = useCommunityDetails(publicKey, setCommunityFound)
|
||||
const { votingContract } = useContracts()
|
||||
const { send } = useContractFunction(votingContract, 'initializeVotingRoom')
|
||||
|
||||
const warning = useProposeWarning(communityFound, availableAmount)
|
||||
|
||||
return (
|
||||
<ColumnFlexDiv>
|
||||
<PublicKeyInput publicKey={publicKey} setPublicKey={setPublicKey} />
|
||||
<ProposingData>
|
||||
{communityFound ? <CardCommunity community={communityFound} /> : loading && publicKey && <CommunitySkeleton />}
|
||||
<WarningWrap>{warning.text && <Warning icon={warning.icon} text={warning.text} />}</WarningWrap>
|
||||
{communityFound && communityFound.validForAddition && publicKey && (
|
||||
<VoteProposeWrap>
|
||||
<VotePropose
|
||||
availableAmount={availableAmount}
|
||||
setProposingAmount={setProposingAmount}
|
||||
proposingAmount={proposingAmount}
|
||||
disabled={!communityFound}
|
||||
/>
|
||||
</VoteProposeWrap>
|
||||
)}
|
||||
{!publicKey && (
|
||||
<ProposingInfo>
|
||||
<span>ℹ️</span>
|
||||
<InfoText>To propose a community, it must have at least 42 members and have a ENS domain.</InfoText>
|
||||
</ProposingInfo>
|
||||
)}
|
||||
</ProposingData>
|
||||
<ProposingBtn
|
||||
disabled={!communityFound || !proposingAmount || !!warning.text}
|
||||
onClick={() => send(1, publicKey, BigNumber.from(proposingAmount))}
|
||||
>
|
||||
Confirm vote to add community
|
||||
</ProposingBtn>
|
||||
</ColumnFlexDiv>
|
||||
)
|
||||
}
|
||||
|
||||
const VoteProposeWrap = styled.div`
|
||||
margin-top: 32px;
|
||||
`
|
||||
|
||||
const ProposingData = styled.div`
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const ProposingInfo = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
& > span {
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
`
|
||||
|
||||
const InfoText = styled.div`
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
letter-spacing: 0.1px;
|
||||
`
|
||||
|
||||
const ProposingBtn = styled(ButtonPrimary)`
|
||||
width: 100%;
|
||||
padding: 11px 0;
|
||||
margin-top: 32px;
|
||||
`
|
||||
const WarningWrap = styled.div`
|
||||
margin: 24px 0;
|
||||
`
|
|
@ -0,0 +1,68 @@
|
|||
import { useContractCall } from '@usedapp/core'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { CommunityDetail } from '../models/community'
|
||||
import { useContracts } from './useContracts'
|
||||
|
||||
export function useProposeWarning(communityFound: CommunityDetail | undefined, availableAmount: number) {
|
||||
const [warning, setWarning] = useState({ icon: '', text: '' })
|
||||
|
||||
const { directoryContract, votingContract } = useContracts()
|
||||
|
||||
const [isCommunityInDirectory] = useContractCall({
|
||||
abi: directoryContract.interface,
|
||||
address: directoryContract.address,
|
||||
method: 'isCommunityInDirectory',
|
||||
args: [communityFound?.publicKey],
|
||||
}) ?? [undefined]
|
||||
|
||||
const [isCommunityUnderVote] = useContractCall({
|
||||
abi: votingContract.interface,
|
||||
address: votingContract.address,
|
||||
method: 'communityVotingId',
|
||||
args: [communityFound?.publicKey],
|
||||
}) ?? [undefined]
|
||||
|
||||
useEffect(() => {
|
||||
const getWarning = () => {
|
||||
if (communityFound) {
|
||||
if (isCommunityInDirectory) {
|
||||
return {
|
||||
icon: '⚠️',
|
||||
text: `${communityFound.name} is already in the communities directory! No need to start a new vote.`,
|
||||
}
|
||||
}
|
||||
if (isCommunityUnderVote && isCommunityUnderVote?.toNumber() > 0) {
|
||||
return { icon: '⚠️', text: `There’s already an ongoing vote to add ${communityFound.name} in the directory!` }
|
||||
}
|
||||
if (!communityFound.ens) {
|
||||
return {
|
||||
icon: '⚠️',
|
||||
text: `${communityFound.name} is not registered in Ethereum Name Service. Only communities with ENS name can be included in the directory.`,
|
||||
}
|
||||
}
|
||||
if (availableAmount < 10000) {
|
||||
return {
|
||||
icon: '💰',
|
||||
text: `Not enough SNT to start a vote for this community. A new vote for ${communityFound.name} requires at least 10,000 SNT available.`,
|
||||
}
|
||||
}
|
||||
if (communityFound.numberOfMembers < 42) {
|
||||
return {
|
||||
icon: '🤏',
|
||||
text: `${communityFound.name} currently only has ${communityFound.numberOfMembers} members. A community needs more than 42 members before a vote to be added to the Status community directory can be proposed.`,
|
||||
}
|
||||
}
|
||||
}
|
||||
return { icon: '', text: '' }
|
||||
}
|
||||
setWarning(getWarning())
|
||||
}, [
|
||||
isCommunityInDirectory,
|
||||
availableAmount,
|
||||
isCommunityUnderVote?.toNumber(),
|
||||
communityFound?.numberOfMembers,
|
||||
communityFound?.ens,
|
||||
])
|
||||
|
||||
return warning
|
||||
}
|
|
@ -2,6 +2,7 @@ import React from 'react'
|
|||
import { Redirect, Route, Switch } from 'react-router'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import styled from 'styled-components'
|
||||
import { ProposeMobile } from '../componentsMobile/ProposeMobile'
|
||||
import { VotingRoomMobile } from '../componentsMobile/VotingRoomMobile'
|
||||
import { DirectoryMobile } from './DirectoryMobile'
|
||||
import { InfoMobile } from './InfoMobile'
|
||||
|
@ -13,6 +14,7 @@ export const MobileRouter = () => (
|
|||
<Switch>
|
||||
<Route exact path="/" render={() => <Redirect to="/votes" />} />
|
||||
<Route exact path="/votingRoom/:id" component={VotingRoomMobile} />
|
||||
<Route exact path="/propose" component={ProposeMobile} />
|
||||
<Route exact path="/votes" component={VotesMobile} />
|
||||
<Route exact path="/directory" component={DirectoryMobile} />
|
||||
<Route exact path="/info" component={InfoMobile} />
|
||||
|
|
|
@ -11,13 +11,15 @@ import styled from 'styled-components'
|
|||
import { VotingCardSkeleton } from '../components/votes/VotingCardSkeleton'
|
||||
import { VotingSortingOptions } from '../constants/SortingOptions'
|
||||
import { VotingCardCover } from '../componentsMobile/VotingCardCover'
|
||||
import { ButtonPrimary } from '../components/Button'
|
||||
import { useHistory } from 'react-router'
|
||||
|
||||
export function VotesMobile() {
|
||||
const [sortedBy, setSortedBy] = useState(VotingSortingEnum.EndingSoonest)
|
||||
const [voteType, setVoteType] = useState('')
|
||||
const [filterKeyword, setFilterKeyword] = useState('')
|
||||
const { roomsToShow, empty } = useVotingCommunities(filterKeyword, voteType, sortedBy)
|
||||
|
||||
const history = useHistory()
|
||||
return (
|
||||
<div>
|
||||
<TopBarMobile
|
||||
|
@ -49,10 +51,24 @@ export function VotesMobile() {
|
|||
{roomsToShow.length === 0 && empty && <VotingEmpty />}
|
||||
{roomsToShow.length === 0 && !empty && <SearchEmpty />}
|
||||
</VotingCardsWrapper>
|
||||
<ProposeButtonWrapper>
|
||||
<ProposeButton onClick={() => history.push('/propose')}>Propose community</ProposeButton>
|
||||
</ProposeButtonWrapper>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ProposeButton = styled(ButtonPrimary)`
|
||||
margin: auto;
|
||||
`
|
||||
const ProposeButtonWrapper = styled.div`
|
||||
display: flex;
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const VotingCardsWrapper = styled.div`
|
||||
padding: 307px 16px 16px;
|
||||
|
||||
|
|
Loading…
Reference in New Issue