Add mobile components (#63)

This commit is contained in:
Maria Rushkova 2021-09-14 13:49:46 +02:00 committed by GitHub
parent 605d5e1950
commit 7d97abdf82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 490 additions and 27 deletions

View File

@ -15,6 +15,7 @@ export function Proposal({ wakuVoting }: ProposalProps) {
<ProposalWrapper>
<ProposalHeader theme={blueTheme} wakuVoting={wakuVoting} />
<ProposalList theme={blueTheme} wakuVoting={wakuVoting} />
{/* <VotingEmpty theme={blueTheme} /> */}
<NotificationItem text={'Proposal you finalized will be settled after 10 confirmations.'} address={'#'} />
</ProposalWrapper>
)
@ -29,12 +30,4 @@ 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;
}
`

View File

@ -1,10 +1,12 @@
import React from 'react'
import { useHistory } from 'react-router'
import styled from 'styled-components'
import { Theme } from '@status-waku-voting/react-components'
import { ProposalInfo } from './ProposalInfo'
import { ProposalVote } from './ProposalVoteCard/ProposalVote'
interface ProposalCardProps {
id: number
theme: Theme
heading: string
text: string
@ -14,9 +16,11 @@ interface ProposalCardProps {
hideModalFunction?: (val: boolean) => void
}
export function ProposalCard({ heading, text, address, vote, voteWinner, theme }: ProposalCardProps) {
export function ProposalCard({ id, heading, text, address, vote, voteWinner, theme }: ProposalCardProps) {
const history = useHistory()
return (
<Card>
<Card onClick={() => history.push(`/votingRoom/${id.toString}`)}>
<ProposalInfo heading={heading} text={text} address={address} />
<ProposalVote vote={vote} voteWinner={voteWinner} address={address} heading={heading} theme={theme} />
</Card>

View File

@ -90,7 +90,7 @@ export function ProposalHeader({ theme, wakuVoting }: ProposalHeaderProps) {
)
}
const Wrapper = styled.div`
export const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;

View File

@ -6,14 +6,15 @@ type ProposalInfoProps = {
heading: string
text: string
address: string
mobileMode?: boolean
}
export function ProposalInfo({ heading, text, address }: ProposalInfoProps) {
export function ProposalInfo({ heading, text, address, mobileMode }: ProposalInfoProps) {
return (
<Card>
<CardHeading>{heading}</CardHeading>
<CardText>{text}</CardText>
<CardViewLink>
<CardText className={mobileMode ? 'mobile' : ''}>{text}</CardText>
<CardViewLink className={mobileMode ? 'mobile' : ''}>
<ViewLink address={address} />
</CardViewLink>
</Card>
@ -68,10 +69,27 @@ export const CardText = styled.div`
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
&.mobile {
@media (max-width: 600px) {
height: 100%;
overflow: unset;
text-overflow: unset;
-webkit-line-clamp: unset;
margin-bottom: 24px;
}
}
`
const CardViewLink = styled.div`
@media (max-width: 768px) {
display: none;
}
&.mobile {
@media (max-width: 600px) {
display: block;
margin-bottom: 37px;
}
}
`

View File

@ -22,7 +22,7 @@ export function ProposalList({ theme, wakuVoting }: ProposalListProps) {
return (
<List>
{votes.map((vote, idx) => {
return <ProposalCard heading={vote[2]} text={vote[3]} address={'#'} theme={theme} key={idx} />
return <ProposalCard heading={vote[2]} text={vote[3]} address={'#'} theme={theme} key={idx} id={idx} />
})}
{votes && votes?.length === 0 && <VotingEmpty wakuVoting={wakuVoting} theme={theme} />}
</List>

View File

@ -105,19 +105,19 @@ export const ProposingInfo = styled.div`
}
`
const InfoText = styled.div`
export const InfoText = styled.div`
font-size: 12px;
line-height: 16px;
letter-spacing: 0.1px;
`
const ProposingInput = styled(TextArea)`
export const ProposingInput = styled(TextArea)`
height: 68px;
`
const ProposingTextInput = styled(ProposingInput)`
export const ProposingTextInput = styled(ProposingInput)`
height: 222px;
`
const Label = styled.label`
export const Label = styled.label`
width: 100%;
font-size: 15px;
line-height: 22px;

View File

@ -23,7 +23,6 @@ export function VoteAnimatedModal({
return (
<VoteConfirm>
<ConfirmText>Your vote {selectedVote === 0 ? 'against' : 'for'} this proposal has been cast!</ConfirmText>
<VoteChart
votesFor={votesFor}
votesAgainst={votesAgainst}

View File

@ -1,4 +1,5 @@
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { useHistory } from 'react-router'
import { useEthers } from '@usedapp/core'
import styled from 'styled-components'
import { CreateButton, Modal, Networks, Theme } from '@status-waku-voting/react-components'
@ -16,14 +17,29 @@ export function VotingEmpty({ wakuVoting, theme }: VotingEmptyProps) {
const [selectConnect, setSelectConnect] = useState(false)
const [showProposeModal, setShowProposeModal] = useState(false)
const [showProposeVoteModal, setShowProposeVoteModal] = useState(false)
const [mobileVersion, setMobileVersion] = useState(false)
const [title, setTitle] = useState('')
const [text, setText] = useState('')
const history = useHistory()
const setNext = (val: boolean) => {
setShowProposeVoteModal(val)
setShowProposeModal(false)
}
useEffect(() => {
const handleResize = () => {
if (window.innerWidth < 600) {
setMobileVersion(true)
} else {
setMobileVersion(false)
}
}
handleResize()
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
}, [])
return (
<VotingEmptyWrap>
<EmptyWrap>
@ -60,9 +76,14 @@ export function VotingEmpty({ wakuVoting, theme }: VotingEmptyProps) {
)}
{account ? (
<CreateButton theme={theme} onClick={() => setShowProposeModal(true)}>
<EmptyCreateButton
theme={theme}
onClick={() => {
mobileVersion ? history.push(`/creation`) : setShowProposeModal(true)
}}
>
Create proposal
</CreateButton>
</EmptyCreateButton>
) : (
<CreateButton
theme={theme}
@ -138,3 +159,9 @@ const EmptyText = styled.p`
margin-bottom: 20px;
}
`
const EmptyCreateButton = styled(CreateButton)`
@media (max-width: 425px) {
position: static;
}
`

View File

@ -0,0 +1,91 @@
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;
}
`
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

@ -0,0 +1,38 @@
import React 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'
type ProposalMainMobileProps = {
wakuVoting: WakuVoting
}
export function ProposalMainMobile({ wakuVoting }: ProposalMainMobileProps) {
return (
<ProposalWrapper>
<ProposalHeaderMobile theme={blueTheme} />
<ProposalList theme={blueTheme} wakuVoting={wakuVoting} />
{/* <VotingEmpty theme={blueTheme} /> */}
<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

@ -0,0 +1,39 @@
import React from 'react'
import { Redirect, Route, Switch } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import styled from 'styled-components'
import { ProposalVoteMobile } from './ProposalVoteMobile'
import { ProposeMobile } from './ProposeMobile'
import { ProposalMainMobile } from './ProposalMainMobile'
import { WakuVoting } from '@status-waku-voting/core'
type ProposalMobileProps = {
wakuVoting: WakuVoting
}
export function ProposalMobile({ wakuVoting }: ProposalMobileProps) {
return (
<BrowserRouter>
<ProposalWrapper>
<Switch>
<Route exact path="/" render={() => <Redirect to="/proposal" />} />
<Route exact path="/votingRoom/:id" component={ProposalVoteMobile} />
<Route exact path="/creation" component={ProposeMobile} />
<Route exact path="/proposal">
<ProposalMainMobile wakuVoting={wakuVoting} />
</Route>
</Switch>
</ProposalWrapper>
</BrowserRouter>
)
}
const ProposalWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
max-width: 1000px;
margin: 0 auto;
width: 100%;
min-height: 100vh;
`

View File

@ -0,0 +1,118 @@
import React, { useState } from 'react'
import { useParams } from 'react-router'
import styled from 'styled-components'
import { useEthers } from '@usedapp/core'
import { FinalBtn, VoteBtnAgainst, VoteBtnFor } from '../Buttons'
import { VoteSubmitButton } from '../ProposalVoteCard/VoteSubmitButton'
import { VoteChart } from '../ProposalVoteCard/VoteChart'
import { ProposalInfo } from '../ProposalInfo'
import { VotePropose } from '../VotePropose'
import { VotesBtns } from '../ProposalVoteCard/ProposalVote'
interface ProposalVoteMobileProps {
vote?: number
voteWinner?: number
votesFor: number
votesAgainst: number
timeLeft: number
availableAmount: number
heading: string
text: string
address: string
}
export function ProposalVoteMobile({
votesFor,
votesAgainst,
timeLeft,
vote,
voteWinner,
address,
heading,
text,
availableAmount,
}: ProposalVoteMobileProps) {
const { id } = useParams<{ id: string }>()
const { account } = useEthers()
const [proposingAmount, setProposingAmount] = useState(0)
const [selectedVoted, setSelectedVoted] = useState(0)
const [mobileVersion, setMobileVersion] = useState(true)
return (
<Card>
<ProposalInfo
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>
<VoteChart votesFor={1865567} votesAgainst={1740235} timeLeft={4855555577} selectedVote={selectedVoted} />
</VoteChartWrap>
{!voteWinner && (
<VotePropose
availableAmount={65245346}
setProposingAmount={setProposingAmount}
proposingAmount={proposingAmount}
/>
)}
<CardButtons>
{voteWinner ? (
<FinalBtn disabled={!account}>Finalize the vote</FinalBtn>
) : (
<VotesBtns>
<VoteBtnAgainst
disabled={!account}
onClick={() => {
setSelectedVoted(0)
}}
>
Vote Against
</VoteBtnAgainst>
<VoteBtnFor
disabled={!account}
onClick={() => {
setSelectedVoted(1)
}}
>
Vote For
</VoteBtnFor>
</VotesBtns>
)}
</CardButtons>
<CardVoteBottom>
{' '}
<VoteSubmitButton votes={2345678} disabled={!account} />
</CardVoteBottom>
</Card>
)
}
export const Card = styled.div`
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
padding: 88px 16px 32px;
`
const CardButtons = styled.div`
width: 100%;
`
const CardVoteBottom = styled.div`
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin-top: 32px;
`
const VoteChartWrap = styled.div`
width: 100%;
margin-bottom: 32px;
`

View File

@ -0,0 +1,121 @@
import { blueTheme } from '@status-waku-voting/react-components/dist/esm/src/style/themes'
import React, { useState } from 'react'
import styled from 'styled-components'
import { useHistory } from 'react-router'
import { ProposingBtn } from '../Buttons'
import { CardHeading, CardText } from '../ProposalInfo'
import { InfoText, Label, ProposingData, ProposingInfo, ProposingInput, ProposingTextInput } from '../ProposeModal'
import { VotePropose } from '../VotePropose'
interface ProposeVoteModalProps {
availableAmount: number
}
export function ProposeMobile({ availableAmount }: ProposeVoteModalProps) {
const insufficientFunds = availableAmount < 10000
const [proposingAmount, setProposingAmount] = useState(0)
const [title, setTitle] = useState('')
const [text, setText] = useState('')
const [customData, setCustomData] = useState(false)
const history = useHistory()
return (
<ProposingDataMobile>
{insufficientFunds && (
<ProposingInfo>
<span></span>
<InfoText>You need at least 10,000 ABC to create a proposal!</InfoText>
</ProposingInfo>
)}
<ProposingCardHeading>Create proposal</ProposingCardHeading>
{!customData && (
<ProposingCustomData>
<Label>
Title
<ProposingInput
cols={2}
maxLength={90}
placeholder="E.g. Change the rate of the token issuance"
value={title}
onInput={(e) => {
setTitle(e.currentTarget.value)
}}
required
/>
</Label>
<Label>
Description
<ProposingTextInput
maxLength={440}
placeholder="Describe your proposal as detailed as you can in 440 characters."
value={text}
onInput={(e) => {
setText(e.currentTarget.value)
}}
required
/>
</Label>
<ProposingBtn
disabled={!text || !title || insufficientFunds}
theme={blueTheme}
onClick={() => setCustomData(true)}
>
Continue
</ProposingBtn>
</ProposingCustomData>
)}
{customData && (
<ProposingCustomData>
<CustomProposingHeading>{title}</CustomProposingHeading>
<ProposingCardText>{text}</ProposingCardText>
<VoteProposeWrap>
<VotePropose
availableAmount={65245555}
setProposingAmount={setProposingAmount}
proposingAmount={proposingAmount}
/>
</VoteProposeWrap>
<ProposingBtn
disabled={proposingAmount === 0}
onClick={() => {
history.push(`/proposal`), setTitle(''), setText('')
}}
>
Create proposal
</ProposingBtn>
</ProposingCustomData>
)}
</ProposingDataMobile>
)
}
const ProposingDataMobile = styled(ProposingData)`
padding: 88px 16px 32px;
margin-top: 0;
`
export const VoteProposeWrap = styled.div`
margin-bottom: 32px;
width: 100%;
`
const ProposingCustomData = styled.div`
width: 100%;
`
const ProposingCardHeading = styled(CardHeading)`
margin-bottom: 16px;
`
const CustomProposingHeading = styled(ProposingCardHeading)`
font-size: 22px;
margin-top: 24px;
`
const ProposingCardText = styled(CardText)`
margin-bottom: 0;
`

View File

@ -1,3 +1,4 @@
import { Proposal } from './components/Proposal'
import { ProposalMobile } from './components/mobile/ProposalMobile'
export { Proposal }
export { Proposal, ProposalMobile }

View File

@ -1,6 +1,6 @@
import React from 'react'
import React, { useEffect, useState } from 'react'
import { useWakuProposal } from '@status-waku-voting/proposal-hooks'
import { Proposal } from '@status-waku-voting/proposal-components'
import { Proposal, ProposalMobile } from '@status-waku-voting/proposal-components'
import { TopBar, GlobalStyle } from '@status-waku-voting/react-components'
import votingIcon from './assets/images/voting.svg'
import styled from 'styled-components'
@ -27,6 +27,20 @@ const config = {
function Proposals() {
const { account, library, activateBrowserWallet, deactivate } = useEthers()
const waku = useWakuProposal()
const [mobileVersion, setMobileVersion] = useState(false)
useEffect(() => {
const handleResize = () => {
if (window.innerWidth < 600) {
setMobileVersion(true)
} else {
setMobileVersion(false)
}
}
handleResize()
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
}, [])
return (
<Wrapper>
@ -38,7 +52,7 @@ function Proposals() {
account={account}
deactivate={deactivate}
/>
{waku && <Proposal wakuVoting={waku} />}
{waku && (mobileVersion ? <ProposalMobile wakuVoting={waku} /> : <Proposal wakuVoting={waku} />)}
</Wrapper>
)
}