Add propose mobile (#143)

This commit is contained in:
Szymon Szlachtowicz 2021-07-22 12:40:25 +02:00 committed by GitHub
parent dc12c4e27b
commit 010b7f56d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 224 additions and 104 deletions

View File

@ -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;
`

View File

@ -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={`Theres 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;
`

View File

@ -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;
`

View File

@ -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: `Theres 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
}

View File

@ -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} />

View File

@ -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;