perf: lazy load vote related routes (#2468)
* perf: lazy load vote related routes * wrap Switch in Suspense * remove exact to match nested routes * fix nested routes * split Landing * fix
This commit is contained in:
parent
0f35f6ee93
commit
222a6d53bc
@ -1,5 +1,7 @@
|
|||||||
|
import Loader from 'components/Loader'
|
||||||
import ApeModeQueryParamReader from 'hooks/useApeModeQueryParamReader'
|
import ApeModeQueryParamReader from 'hooks/useApeModeQueryParamReader'
|
||||||
import { Route, Switch } from 'react-router-dom'
|
import { lazy, Suspense } from 'react'
|
||||||
|
import { Redirect, Route, Switch } from 'react-router-dom'
|
||||||
import styled from 'styled-components/macro'
|
import styled from 'styled-components/macro'
|
||||||
|
|
||||||
import GoogleAnalyticsReporter from '../components/analytics/GoogleAnalyticsReporter'
|
import GoogleAnalyticsReporter from '../components/analytics/GoogleAnalyticsReporter'
|
||||||
@ -15,7 +17,6 @@ import DarkModeQueryParamReader from '../theme/DarkModeQueryParamReader'
|
|||||||
import AddLiquidity from './AddLiquidity'
|
import AddLiquidity from './AddLiquidity'
|
||||||
import { RedirectDuplicateTokenIds } from './AddLiquidity/redirects'
|
import { RedirectDuplicateTokenIds } from './AddLiquidity/redirects'
|
||||||
import { RedirectDuplicateTokenIdsV2 } from './AddLiquidityV2/redirects'
|
import { RedirectDuplicateTokenIdsV2 } from './AddLiquidityV2/redirects'
|
||||||
import CreateProposal from './CreateProposal'
|
|
||||||
import Earn from './Earn'
|
import Earn from './Earn'
|
||||||
import Manage from './Earn/Manage'
|
import Manage from './Earn/Manage'
|
||||||
import MigrateV2 from './MigrateV2'
|
import MigrateV2 from './MigrateV2'
|
||||||
@ -28,8 +29,8 @@ import RemoveLiquidity from './RemoveLiquidity'
|
|||||||
import RemoveLiquidityV3 from './RemoveLiquidity/V3'
|
import RemoveLiquidityV3 from './RemoveLiquidity/V3'
|
||||||
import Swap from './Swap'
|
import Swap from './Swap'
|
||||||
import { OpenClaimAddressModalAndRedirectToSwap, RedirectPathToSwapOnly, RedirectToSwap } from './Swap/redirects'
|
import { OpenClaimAddressModalAndRedirectToSwap, RedirectPathToSwapOnly, RedirectToSwap } from './Swap/redirects'
|
||||||
import Vote from './Vote'
|
|
||||||
import VotePage from './Vote/VotePage'
|
const Vote = lazy(() => import('./Vote'))
|
||||||
|
|
||||||
const AppWrapper = styled.div`
|
const AppWrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -85,46 +86,54 @@ export default function App() {
|
|||||||
<Popups />
|
<Popups />
|
||||||
<Polling />
|
<Polling />
|
||||||
<TopLevelModals />
|
<TopLevelModals />
|
||||||
<Switch>
|
<Suspense fallback={<Loader />}>
|
||||||
<Route exact strict path="/vote" component={Vote} />
|
<Switch>
|
||||||
<Route exact strict path="/vote/:governorIndex/:id" component={VotePage} />
|
<Route strict path="/vote" component={Vote} />
|
||||||
<Route exact strict path="/claim" component={OpenClaimAddressModalAndRedirectToSwap} />
|
<Route exact strict path="/create-proposal">
|
||||||
<Route exact strict path="/uni" component={Earn} />
|
<Redirect to="/vote/create-proposal" />
|
||||||
<Route exact strict path="/uni/:currencyIdA/:currencyIdB" component={Manage} />
|
</Route>
|
||||||
|
<Route exact strict path="/claim" component={OpenClaimAddressModalAndRedirectToSwap} />
|
||||||
|
<Route exact strict path="/uni" component={Earn} />
|
||||||
|
<Route exact strict path="/uni/:currencyIdA/:currencyIdB" component={Manage} />
|
||||||
|
|
||||||
<Route exact strict path="/send" component={RedirectPathToSwapOnly} />
|
<Route exact strict path="/send" component={RedirectPathToSwapOnly} />
|
||||||
<Route exact strict path="/swap/:outputCurrency" component={RedirectToSwap} />
|
<Route exact strict path="/swap/:outputCurrency" component={RedirectToSwap} />
|
||||||
<Route exact strict path="/swap" component={Swap} />
|
<Route exact strict path="/swap" component={Swap} />
|
||||||
|
|
||||||
<Route exact strict path="/pool/v2/find" component={PoolFinder} />
|
<Route exact strict path="/pool/v2/find" component={PoolFinder} />
|
||||||
<Route exact strict path="/pool/v2" component={PoolV2} />
|
<Route exact strict path="/pool/v2" component={PoolV2} />
|
||||||
<Route exact strict path="/pool" component={Pool} />
|
<Route exact strict path="/pool" component={Pool} />
|
||||||
<Route exact strict path="/pool/:tokenId" component={PositionPage} />
|
<Route exact strict path="/pool/:tokenId" component={PositionPage} />
|
||||||
|
|
||||||
<Route exact strict path="/add/v2/:currencyIdA?/:currencyIdB?" component={RedirectDuplicateTokenIdsV2} />
|
<Route
|
||||||
<Route
|
exact
|
||||||
exact
|
strict
|
||||||
strict
|
path="/add/v2/:currencyIdA?/:currencyIdB?"
|
||||||
path="/add/:currencyIdA?/:currencyIdB?/:feeAmount?"
|
component={RedirectDuplicateTokenIdsV2}
|
||||||
component={RedirectDuplicateTokenIds}
|
/>
|
||||||
/>
|
<Route
|
||||||
|
exact
|
||||||
|
strict
|
||||||
|
path="/add/:currencyIdA?/:currencyIdB?/:feeAmount?"
|
||||||
|
component={RedirectDuplicateTokenIds}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
strict
|
strict
|
||||||
path="/increase/:currencyIdA?/:currencyIdB?/:feeAmount?/:tokenId?"
|
path="/increase/:currencyIdA?/:currencyIdB?/:feeAmount?/:tokenId?"
|
||||||
component={AddLiquidity}
|
component={AddLiquidity}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route exact strict path="/remove/v2/:currencyIdA/:currencyIdB" component={RemoveLiquidity} />
|
<Route exact strict path="/remove/v2/:currencyIdA/:currencyIdB" component={RemoveLiquidity} />
|
||||||
<Route exact strict path="/remove/:tokenId" component={RemoveLiquidityV3} />
|
<Route exact strict path="/remove/:tokenId" component={RemoveLiquidityV3} />
|
||||||
|
|
||||||
<Route exact strict path="/migrate/v2" component={MigrateV2} />
|
<Route exact strict path="/migrate/v2" component={MigrateV2} />
|
||||||
<Route exact strict path="/migrate/v2/:address" component={MigrateV2Pair} />
|
<Route exact strict path="/migrate/v2/:address" component={MigrateV2Pair} />
|
||||||
|
|
||||||
<Route exact strict path="/create-proposal" component={CreateProposal} />
|
<Route component={RedirectPathToSwapOnly} />
|
||||||
<Route component={RedirectPathToSwapOnly} />
|
</Switch>
|
||||||
</Switch>
|
</Suspense>
|
||||||
<Marginer />
|
<Marginer />
|
||||||
</BodyWrapper>
|
</BodyWrapper>
|
||||||
</AppWrapper>
|
</AppWrapper>
|
||||||
|
257
src/pages/Vote/Landing.tsx
Normal file
257
src/pages/Vote/Landing.tsx
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
import { Trans } from '@lingui/macro'
|
||||||
|
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||||
|
import { ButtonPrimary } from 'components/Button'
|
||||||
|
import { AutoColumn } from 'components/Column'
|
||||||
|
import { CardBGImage, CardNoise, CardSection, DataCard } from 'components/earn/styled'
|
||||||
|
import FormattedCurrencyAmount from 'components/FormattedCurrencyAmount'
|
||||||
|
import Loader from 'components/Loader'
|
||||||
|
import { AutoRow, RowBetween, RowFixed } from 'components/Row'
|
||||||
|
import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
|
||||||
|
import DelegateModal from 'components/vote/DelegateModal'
|
||||||
|
import ProposalEmptyState from 'components/vote/ProposalEmptyState'
|
||||||
|
import { useActiveWeb3React } from 'hooks/web3'
|
||||||
|
import JSBI from 'jsbi'
|
||||||
|
import { darken } from 'polished'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
import { Button } from 'rebass/styled-components'
|
||||||
|
import { useModalOpen, useToggleDelegateModal } from 'state/application/hooks'
|
||||||
|
import { ApplicationModal } from 'state/application/reducer'
|
||||||
|
import { ProposalData } from 'state/governance/hooks'
|
||||||
|
import { useAllProposalData, useUserDelegatee, useUserVotes } from 'state/governance/hooks'
|
||||||
|
import { useTokenBalance } from 'state/wallet/hooks'
|
||||||
|
import styled from 'styled-components/macro'
|
||||||
|
import { ExternalLink, TYPE } from 'theme'
|
||||||
|
import { shortenAddress } from 'utils'
|
||||||
|
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
|
||||||
|
|
||||||
|
import { ZERO_ADDRESS } from '../../constants/misc'
|
||||||
|
import { UNI } from '../../constants/tokens'
|
||||||
|
import { ProposalStatus } from './styled'
|
||||||
|
|
||||||
|
const PageWrapper = styled(AutoColumn)``
|
||||||
|
|
||||||
|
const TopSection = styled(AutoColumn)`
|
||||||
|
max-width: 640px;
|
||||||
|
width: 100%;
|
||||||
|
`
|
||||||
|
|
||||||
|
const Proposal = styled(Button)`
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 48px 1fr 120px;
|
||||||
|
align-items: center;
|
||||||
|
text-align: left;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: ${({ theme }) => theme.text1};
|
||||||
|
text-decoration: none;
|
||||||
|
background-color: ${({ theme }) => theme.bg1};
|
||||||
|
&:focus {
|
||||||
|
background-color: ${({ theme }) => darken(0.05, theme.bg1)};
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background-color: ${({ theme }) => darken(0.05, theme.bg1)};
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const ProposalNumber = styled.span`
|
||||||
|
opacity: 0.6;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ProposalTitle = styled.span`
|
||||||
|
font-weight: 600;
|
||||||
|
`
|
||||||
|
|
||||||
|
const VoteCard = styled(DataCard)`
|
||||||
|
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #27ae60 0%, #000000 100%);
|
||||||
|
overflow: hidden;
|
||||||
|
`
|
||||||
|
|
||||||
|
const WrapSmall = styled(RowBetween)`
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||||
|
flex-wrap: wrap;
|
||||||
|
`};
|
||||||
|
`
|
||||||
|
|
||||||
|
const TextButton = styled(TYPE.main)`
|
||||||
|
color: ${({ theme }) => theme.primary1};
|
||||||
|
:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const AddressButton = styled.div`
|
||||||
|
border: 1px solid ${({ theme }) => theme.bg3};
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
`
|
||||||
|
|
||||||
|
const StyledExternalLink = styled(ExternalLink)`
|
||||||
|
color: ${({ theme }) => theme.text1};
|
||||||
|
`
|
||||||
|
|
||||||
|
export default function Landing() {
|
||||||
|
const { account, chainId } = useActiveWeb3React()
|
||||||
|
|
||||||
|
// toggle for showing delegation modal
|
||||||
|
const showDelegateModal = useModalOpen(ApplicationModal.DELEGATE)
|
||||||
|
const toggleDelegateModal = useToggleDelegateModal()
|
||||||
|
|
||||||
|
// get data to list all proposals
|
||||||
|
const { data: allProposals, loading: loadingProposals } = useAllProposalData()
|
||||||
|
|
||||||
|
// user data
|
||||||
|
const { loading: loadingAvailableVotes, votes: availableVotes } = useUserVotes()
|
||||||
|
const uniBalance: CurrencyAmount<Token> | undefined = useTokenBalance(
|
||||||
|
account ?? undefined,
|
||||||
|
chainId ? UNI[chainId] : undefined
|
||||||
|
)
|
||||||
|
const userDelegatee: string | undefined = useUserDelegatee()
|
||||||
|
|
||||||
|
// show delegation option if they have have a balance, but have not delegated
|
||||||
|
const showUnlockVoting = Boolean(
|
||||||
|
uniBalance && JSBI.notEqual(uniBalance.quotient, JSBI.BigInt(0)) && userDelegatee === ZERO_ADDRESS
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PageWrapper gap="lg" justify="center">
|
||||||
|
<DelegateModal
|
||||||
|
isOpen={showDelegateModal}
|
||||||
|
onDismiss={toggleDelegateModal}
|
||||||
|
title={showUnlockVoting ? <Trans>Unlock Votes</Trans> : <Trans>Update Delegation</Trans>}
|
||||||
|
/>
|
||||||
|
<TopSection gap="md">
|
||||||
|
<VoteCard>
|
||||||
|
<CardBGImage />
|
||||||
|
<CardNoise />
|
||||||
|
<CardSection>
|
||||||
|
<AutoColumn gap="md">
|
||||||
|
<RowBetween>
|
||||||
|
<TYPE.white fontWeight={600}>
|
||||||
|
<Trans>Uniswap Governance</Trans>
|
||||||
|
</TYPE.white>
|
||||||
|
</RowBetween>
|
||||||
|
<RowBetween>
|
||||||
|
<TYPE.white fontSize={14}>
|
||||||
|
<Trans>
|
||||||
|
UNI tokens represent voting shares in Uniswap governance. You can vote on each proposal yourself
|
||||||
|
or delegate your votes to a third party.
|
||||||
|
</Trans>
|
||||||
|
</TYPE.white>
|
||||||
|
</RowBetween>
|
||||||
|
<ExternalLink
|
||||||
|
style={{ color: 'white', textDecoration: 'underline' }}
|
||||||
|
href="https://uniswap.org/blog/uni"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<TYPE.white fontSize={14}>
|
||||||
|
<Trans>Read more about Uniswap governance</Trans>
|
||||||
|
</TYPE.white>
|
||||||
|
</ExternalLink>
|
||||||
|
</AutoColumn>
|
||||||
|
</CardSection>
|
||||||
|
<CardBGImage />
|
||||||
|
<CardNoise />
|
||||||
|
</VoteCard>
|
||||||
|
</TopSection>
|
||||||
|
<TopSection gap="2px">
|
||||||
|
<WrapSmall>
|
||||||
|
<TYPE.mediumHeader style={{ margin: '0.5rem 0.5rem 0.5rem 0', flexShrink: 0 }}>
|
||||||
|
<Trans>Proposals</Trans>
|
||||||
|
</TYPE.mediumHeader>
|
||||||
|
<AutoRow gap="6px" justify="flex-end">
|
||||||
|
{loadingProposals || loadingAvailableVotes ? <Loader /> : null}
|
||||||
|
{showUnlockVoting ? (
|
||||||
|
<ButtonPrimary
|
||||||
|
style={{ width: 'fit-content' }}
|
||||||
|
padding="8px"
|
||||||
|
$borderRadius="8px"
|
||||||
|
onClick={toggleDelegateModal}
|
||||||
|
>
|
||||||
|
<Trans>Unlock Voting</Trans>
|
||||||
|
</ButtonPrimary>
|
||||||
|
) : availableVotes && JSBI.notEqual(JSBI.BigInt(0), availableVotes?.quotient) ? (
|
||||||
|
<TYPE.body fontWeight={500} mr="6px">
|
||||||
|
<Trans>
|
||||||
|
<FormattedCurrencyAmount currencyAmount={availableVotes} /> Votes
|
||||||
|
</Trans>
|
||||||
|
</TYPE.body>
|
||||||
|
) : uniBalance &&
|
||||||
|
userDelegatee &&
|
||||||
|
userDelegatee !== ZERO_ADDRESS &&
|
||||||
|
JSBI.notEqual(JSBI.BigInt(0), uniBalance?.quotient) ? (
|
||||||
|
<TYPE.body fontWeight={500} mr="6px">
|
||||||
|
<Trans>
|
||||||
|
<FormattedCurrencyAmount currencyAmount={uniBalance} /> Votes
|
||||||
|
</Trans>
|
||||||
|
</TYPE.body>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
<ButtonPrimary
|
||||||
|
as={Link}
|
||||||
|
to="/create-proposal"
|
||||||
|
style={{ width: 'fit-content', borderRadius: '8px' }}
|
||||||
|
padding="8px"
|
||||||
|
>
|
||||||
|
<Trans>Create Proposal</Trans>
|
||||||
|
</ButtonPrimary>
|
||||||
|
</AutoRow>
|
||||||
|
</WrapSmall>
|
||||||
|
{!showUnlockVoting && (
|
||||||
|
<RowBetween>
|
||||||
|
<div />
|
||||||
|
{userDelegatee && userDelegatee !== ZERO_ADDRESS ? (
|
||||||
|
<RowFixed>
|
||||||
|
<TYPE.body fontWeight={500} mr="4px">
|
||||||
|
<Trans>Delegated to:</Trans>
|
||||||
|
</TYPE.body>
|
||||||
|
<AddressButton>
|
||||||
|
<StyledExternalLink
|
||||||
|
href={getExplorerLink(1, userDelegatee, ExplorerDataType.ADDRESS)}
|
||||||
|
style={{ margin: '0 4px' }}
|
||||||
|
>
|
||||||
|
{userDelegatee === account ? <Trans>Self</Trans> : shortenAddress(userDelegatee)}
|
||||||
|
</StyledExternalLink>
|
||||||
|
<TextButton onClick={toggleDelegateModal} style={{ marginLeft: '4px' }}>
|
||||||
|
<Trans>(edit)</Trans>
|
||||||
|
</TextButton>
|
||||||
|
</AddressButton>
|
||||||
|
</RowFixed>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</RowBetween>
|
||||||
|
)}
|
||||||
|
{allProposals?.length === 0 && <ProposalEmptyState />}
|
||||||
|
{allProposals
|
||||||
|
?.slice(0)
|
||||||
|
?.reverse()
|
||||||
|
?.map((p: ProposalData) => {
|
||||||
|
return (
|
||||||
|
<Proposal as={Link} to={`/vote/${p.governorIndex}/${p.id}`} key={`${p.governorIndex}${p.id}`}>
|
||||||
|
<ProposalNumber>
|
||||||
|
{p.governorIndex}.{p.id}
|
||||||
|
</ProposalNumber>
|
||||||
|
<ProposalTitle>{p.title}</ProposalTitle>
|
||||||
|
<ProposalStatus status={p.status} />
|
||||||
|
</Proposal>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</TopSection>
|
||||||
|
<TYPE.subHeader color="text3">
|
||||||
|
<Trans>A minimum threshold of 0.25% of the total UNI supply is required to submit proposals</Trans>
|
||||||
|
</TYPE.subHeader>
|
||||||
|
</PageWrapper>
|
||||||
|
<SwitchLocaleLink />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -1,257 +1,15 @@
|
|||||||
import { Trans } from '@lingui/macro'
|
import CreateProposal from 'pages/CreateProposal'
|
||||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
import { Route } from 'react-router-dom'
|
||||||
import { ButtonPrimary } from 'components/Button'
|
|
||||||
import { AutoColumn } from 'components/Column'
|
|
||||||
import { CardBGImage, CardNoise, CardSection, DataCard } from 'components/earn/styled'
|
|
||||||
import FormattedCurrencyAmount from 'components/FormattedCurrencyAmount'
|
|
||||||
import Loader from 'components/Loader'
|
|
||||||
import { AutoRow, RowBetween, RowFixed } from 'components/Row'
|
|
||||||
import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
|
|
||||||
import DelegateModal from 'components/vote/DelegateModal'
|
|
||||||
import ProposalEmptyState from 'components/vote/ProposalEmptyState'
|
|
||||||
import { useActiveWeb3React } from 'hooks/web3'
|
|
||||||
import JSBI from 'jsbi'
|
|
||||||
import { darken } from 'polished'
|
|
||||||
import { Link } from 'react-router-dom'
|
|
||||||
import { Button } from 'rebass/styled-components'
|
|
||||||
import { useModalOpen, useToggleDelegateModal } from 'state/application/hooks'
|
|
||||||
import { ApplicationModal } from 'state/application/reducer'
|
|
||||||
import { ProposalData, useAllProposalData, useUserDelegatee, useUserVotes } from 'state/governance/hooks'
|
|
||||||
import { useTokenBalance } from 'state/wallet/hooks'
|
|
||||||
import styled from 'styled-components/macro'
|
|
||||||
import { ExternalLink, TYPE } from 'theme'
|
|
||||||
import { shortenAddress } from 'utils'
|
|
||||||
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
|
|
||||||
|
|
||||||
import { ZERO_ADDRESS } from '../../constants/misc'
|
import Landing from './Landing'
|
||||||
import { UNI } from '../../constants/tokens'
|
import VotePage from './VotePage'
|
||||||
import { ProposalStatus } from './styled'
|
|
||||||
|
|
||||||
const PageWrapper = styled(AutoColumn)``
|
|
||||||
|
|
||||||
const TopSection = styled(AutoColumn)`
|
|
||||||
max-width: 640px;
|
|
||||||
width: 100%;
|
|
||||||
`
|
|
||||||
|
|
||||||
const Proposal = styled(Button)`
|
|
||||||
padding: 0.75rem 1rem;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 1rem;
|
|
||||||
border-radius: 12px;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 48px 1fr 120px;
|
|
||||||
align-items: center;
|
|
||||||
text-align: left;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
|
||||||
color: ${({ theme }) => theme.text1};
|
|
||||||
text-decoration: none;
|
|
||||||
background-color: ${({ theme }) => theme.bg1};
|
|
||||||
&:focus {
|
|
||||||
background-color: ${({ theme }) => darken(0.05, theme.bg1)};
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
background-color: ${({ theme }) => darken(0.05, theme.bg1)};
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const ProposalNumber = styled.span`
|
|
||||||
opacity: 0.6;
|
|
||||||
`
|
|
||||||
|
|
||||||
const ProposalTitle = styled.span`
|
|
||||||
font-weight: 600;
|
|
||||||
`
|
|
||||||
|
|
||||||
const VoteCard = styled(DataCard)`
|
|
||||||
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #27ae60 0%, #000000 100%);
|
|
||||||
overflow: hidden;
|
|
||||||
`
|
|
||||||
|
|
||||||
const WrapSmall = styled(RowBetween)`
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
|
||||||
flex-wrap: wrap;
|
|
||||||
`};
|
|
||||||
`
|
|
||||||
|
|
||||||
const TextButton = styled(TYPE.main)`
|
|
||||||
color: ${({ theme }) => theme.primary1};
|
|
||||||
:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const AddressButton = styled.div`
|
|
||||||
border: 1px solid ${({ theme }) => theme.bg3};
|
|
||||||
padding: 2px 4px;
|
|
||||||
border-radius: 8px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
`
|
|
||||||
|
|
||||||
const StyledExternalLink = styled(ExternalLink)`
|
|
||||||
color: ${({ theme }) => theme.text1};
|
|
||||||
`
|
|
||||||
|
|
||||||
export default function Vote() {
|
export default function Vote() {
|
||||||
const { account, chainId } = useActiveWeb3React()
|
|
||||||
|
|
||||||
// toggle for showing delegation modal
|
|
||||||
const showDelegateModal = useModalOpen(ApplicationModal.DELEGATE)
|
|
||||||
const toggleDelegateModal = useToggleDelegateModal()
|
|
||||||
|
|
||||||
// get data to list all proposals
|
|
||||||
const { data: allProposals, loading: loadingProposals } = useAllProposalData()
|
|
||||||
|
|
||||||
// user data
|
|
||||||
const { loading: loadingAvailableVotes, votes: availableVotes } = useUserVotes()
|
|
||||||
const uniBalance: CurrencyAmount<Token> | undefined = useTokenBalance(
|
|
||||||
account ?? undefined,
|
|
||||||
chainId ? UNI[chainId] : undefined
|
|
||||||
)
|
|
||||||
const userDelegatee: string | undefined = useUserDelegatee()
|
|
||||||
|
|
||||||
// show delegation option if they have have a balance, but have not delegated
|
|
||||||
const showUnlockVoting = Boolean(
|
|
||||||
uniBalance && JSBI.notEqual(uniBalance.quotient, JSBI.BigInt(0)) && userDelegatee === ZERO_ADDRESS
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageWrapper gap="lg" justify="center">
|
<Route exact strict path="/vote/:governorIndex/:id" component={VotePage} />
|
||||||
<DelegateModal
|
<Route exact strict path="/vote/create-proposal" component={CreateProposal} />
|
||||||
isOpen={showDelegateModal}
|
<Route exact strict path="/vote" component={Landing} />
|
||||||
onDismiss={toggleDelegateModal}
|
|
||||||
title={showUnlockVoting ? <Trans>Unlock Votes</Trans> : <Trans>Update Delegation</Trans>}
|
|
||||||
/>
|
|
||||||
<TopSection gap="md">
|
|
||||||
<VoteCard>
|
|
||||||
<CardBGImage />
|
|
||||||
<CardNoise />
|
|
||||||
<CardSection>
|
|
||||||
<AutoColumn gap="md">
|
|
||||||
<RowBetween>
|
|
||||||
<TYPE.white fontWeight={600}>
|
|
||||||
<Trans>Uniswap Governance</Trans>
|
|
||||||
</TYPE.white>
|
|
||||||
</RowBetween>
|
|
||||||
<RowBetween>
|
|
||||||
<TYPE.white fontSize={14}>
|
|
||||||
<Trans>
|
|
||||||
UNI tokens represent voting shares in Uniswap governance. You can vote on each proposal yourself
|
|
||||||
or delegate your votes to a third party.
|
|
||||||
</Trans>
|
|
||||||
</TYPE.white>
|
|
||||||
</RowBetween>
|
|
||||||
<ExternalLink
|
|
||||||
style={{ color: 'white', textDecoration: 'underline' }}
|
|
||||||
href="https://uniswap.org/blog/uni"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<TYPE.white fontSize={14}>
|
|
||||||
<Trans>Read more about Uniswap governance</Trans>
|
|
||||||
</TYPE.white>
|
|
||||||
</ExternalLink>
|
|
||||||
</AutoColumn>
|
|
||||||
</CardSection>
|
|
||||||
<CardBGImage />
|
|
||||||
<CardNoise />
|
|
||||||
</VoteCard>
|
|
||||||
</TopSection>
|
|
||||||
<TopSection gap="2px">
|
|
||||||
<WrapSmall>
|
|
||||||
<TYPE.mediumHeader style={{ margin: '0.5rem 0.5rem 0.5rem 0', flexShrink: 0 }}>
|
|
||||||
<Trans>Proposals</Trans>
|
|
||||||
</TYPE.mediumHeader>
|
|
||||||
<AutoRow gap="6px" justify="flex-end">
|
|
||||||
{loadingProposals || loadingAvailableVotes ? <Loader /> : null}
|
|
||||||
{showUnlockVoting ? (
|
|
||||||
<ButtonPrimary
|
|
||||||
style={{ width: 'fit-content' }}
|
|
||||||
padding="8px"
|
|
||||||
$borderRadius="8px"
|
|
||||||
onClick={toggleDelegateModal}
|
|
||||||
>
|
|
||||||
<Trans>Unlock Voting</Trans>
|
|
||||||
</ButtonPrimary>
|
|
||||||
) : availableVotes && JSBI.notEqual(JSBI.BigInt(0), availableVotes?.quotient) ? (
|
|
||||||
<TYPE.body fontWeight={500} mr="6px">
|
|
||||||
<Trans>
|
|
||||||
<FormattedCurrencyAmount currencyAmount={availableVotes} /> Votes
|
|
||||||
</Trans>
|
|
||||||
</TYPE.body>
|
|
||||||
) : uniBalance &&
|
|
||||||
userDelegatee &&
|
|
||||||
userDelegatee !== ZERO_ADDRESS &&
|
|
||||||
JSBI.notEqual(JSBI.BigInt(0), uniBalance?.quotient) ? (
|
|
||||||
<TYPE.body fontWeight={500} mr="6px">
|
|
||||||
<Trans>
|
|
||||||
<FormattedCurrencyAmount currencyAmount={uniBalance} /> Votes
|
|
||||||
</Trans>
|
|
||||||
</TYPE.body>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
<ButtonPrimary
|
|
||||||
as={Link}
|
|
||||||
to="/create-proposal"
|
|
||||||
style={{ width: 'fit-content', borderRadius: '8px' }}
|
|
||||||
padding="8px"
|
|
||||||
>
|
|
||||||
<Trans>Create Proposal</Trans>
|
|
||||||
</ButtonPrimary>
|
|
||||||
</AutoRow>
|
|
||||||
</WrapSmall>
|
|
||||||
{!showUnlockVoting && (
|
|
||||||
<RowBetween>
|
|
||||||
<div />
|
|
||||||
{userDelegatee && userDelegatee !== ZERO_ADDRESS ? (
|
|
||||||
<RowFixed>
|
|
||||||
<TYPE.body fontWeight={500} mr="4px">
|
|
||||||
<Trans>Delegated to:</Trans>
|
|
||||||
</TYPE.body>
|
|
||||||
<AddressButton>
|
|
||||||
<StyledExternalLink
|
|
||||||
href={getExplorerLink(1, userDelegatee, ExplorerDataType.ADDRESS)}
|
|
||||||
style={{ margin: '0 4px' }}
|
|
||||||
>
|
|
||||||
{userDelegatee === account ? <Trans>Self</Trans> : shortenAddress(userDelegatee)}
|
|
||||||
</StyledExternalLink>
|
|
||||||
<TextButton onClick={toggleDelegateModal} style={{ marginLeft: '4px' }}>
|
|
||||||
<Trans>(edit)</Trans>
|
|
||||||
</TextButton>
|
|
||||||
</AddressButton>
|
|
||||||
</RowFixed>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</RowBetween>
|
|
||||||
)}
|
|
||||||
{allProposals?.length === 0 && <ProposalEmptyState />}
|
|
||||||
{allProposals
|
|
||||||
?.slice(0)
|
|
||||||
?.reverse()
|
|
||||||
?.map((p: ProposalData) => {
|
|
||||||
return (
|
|
||||||
<Proposal as={Link} to={`/vote/${p.governorIndex}/${p.id}`} key={`${p.governorIndex}${p.id}`}>
|
|
||||||
<ProposalNumber>
|
|
||||||
{p.governorIndex}.{p.id}
|
|
||||||
</ProposalNumber>
|
|
||||||
<ProposalTitle>{p.title}</ProposalTitle>
|
|
||||||
<ProposalStatus status={p.status} />
|
|
||||||
</Proposal>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</TopSection>
|
|
||||||
<TYPE.subHeader color="text3">
|
|
||||||
<Trans>A minimum threshold of 0.25% of the total UNI supply is required to submit proposals</Trans>
|
|
||||||
</TYPE.subHeader>
|
|
||||||
</PageWrapper>
|
|
||||||
<SwitchLocaleLink />
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user