Commit 6fd9808e authored by Ian Lapham's avatar Ian Lapham Committed by GitHub

Style updates merge with storage updates (#710)


* Add advanced user toggle

* UX stuff

* Remove unused imported checks

* Design tweaks and selection card

* remove unused imports

* Cleanup

* fix merge
Co-authored-by: default avatarCallil Capuozzo <callil.capuozzo@gmail.com>
parent c3d8bc7e
public/favicon.ico

31 KB | W: | H:

public/favicon.ico

325 Bytes | W: | H:

public/favicon.ico
public/favicon.ico
public/favicon.ico
public/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
......@@ -20,7 +20,8 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Uniswap</title>
<title>Uniswap Interface</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -8,6 +8,7 @@ import { CheckCircle, Copy } from 'react-feather'
const CopyIcon = styled(Link)`
color: ${({ theme }) => theme.text4};
flex-shrink: 0;
display: flex;
margin-right: 1rem;
margin-left: 0.5rem;
text-decoration: none;
......@@ -24,11 +25,12 @@ const TransactionStatusText = styled.span`
align-items: center;
`
export default function CopyHelper({ toCopy }) {
export default function CopyHelper(props, { toCopy }) {
const [isCopied, setCopied] = useCopyClipboard()
return (
<CopyIcon onClick={() => setCopied(toCopy)}>
{props.children}
{isCopied ? (
<TransactionStatusText>
<CheckCircle size={'16'} />
......
......@@ -4,6 +4,7 @@ import { useWeb3React } from '../../hooks'
import { isMobile } from 'react-device-detect'
import Copy from './Copy'
import Transaction from './Transaction'
import { SUPPORTED_WALLETS } from '../../constants'
import { ReactComponent as Close } from '../../assets/images/x.svg'
import { getEtherscanLink } from '../../utils'
......@@ -14,26 +15,9 @@ import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
import PortisIcon from '../../assets/images/portisIcon.png'
import Identicon from '../Identicon'
import { Link } from '../../theme'
const OptionButton = styled.div`
${({ theme }) => theme.flexColumnNoWrap}
justify-content: center;
align-items: center;
border-radius: 20px;
border: 1px solid ${({ theme }) => theme.blue1};
color: ${({ theme }) => theme.blue1};
padding: 8px 24px;
&:hover {
border: 1px solid ${({ theme }) => theme.blue3};
cursor: pointer;
}
import { ButtonLight } from '../Button'
${({ theme }) => theme.mediaWidth.upToMedium`
font-size: 12px;
`};
`
import { Link, TYPE } from '../../theme'
const HeaderRow = styled.div`
${({ theme }) => theme.flexRowNoWrap};
......@@ -67,7 +51,8 @@ const UpperSection = styled.div`
const InfoCard = styled.div`
padding: 1rem;
border: 1px solid ${({ theme }) => theme.bg4};
/* border: 1px solid ${({ theme }) => theme.bg2}; */
background-color: ${({ theme }) => theme.bg2};
border-radius: 20px;
`
......@@ -168,7 +153,7 @@ const ConnectButtonRow = styled.div`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
justify-content: center;
margin: 30px;
margin: 10px 0;
`
const StyledLink = styled(Link)`
......@@ -352,26 +337,28 @@ export default function AccountDetails({
{!(isMobile && (window.web3 || window.ethereum)) && (
<ConnectButtonRow>
<OptionButton
<ButtonLight
padding={'8px 12px'}
width={240}
onClick={() => {
openOptions()
}}
>
Connect to a different wallet
</OptionButton>
</ButtonLight>
</ConnectButtonRow>
)}
</AccountSection>
</UpperSection>
{!!pendingTransactions.length || !!confirmedTransactions.length ? (
<LowerSection>
<h5>Recent Transactions</h5>
<TYPE.body>Recent Transactions</TYPE.body>
{renderTransactions(pendingTransactions, true)}
{renderTransactions(confirmedTransactions, false)}
</LowerSection>
) : (
<LowerSection>
<h5>Your transactions will appear here...</h5>
<TYPE.body>Your transactions will appear here...</TYPE.body>
</LowerSection>
)}
</>
......
This diff is collapsed.
......@@ -16,6 +16,10 @@ const Base = styled(RebassButton)`
outline: none;
border: 1px solid transparent;
color: white;
display: flex;
justify-content: center;
flex-wrap: nowrap;
align-items: center;
cursor: pointer;
&:disabled {
cursor: auto;
......@@ -51,7 +55,7 @@ export const ButtonPrimary = styled(Base)`
export const ButtonLight = styled(Base)`
background-color: ${({ theme }) => theme.blue5};
color: ${({ theme }) => theme.blue1};
font-size: 20px;
font-size: 16px;
font-weight: 500;
&:focus {
box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.05, theme.blue5)};
......@@ -66,26 +70,44 @@ export const ButtonLight = styled(Base)`
}
`
export const ButtonGray = styled(Base)`
background-color: ${({ theme }) => theme.bg3};
color: ${({ theme }) => theme.text2};
font-size: 16px;
font-weight: 500;
&:focus {
box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
}
&:hover {
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
}
&:active {
box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.1, theme.bg2)};
background-color: ${({ theme, disabled }) => !disabled && darken(0.1, theme.bg2)};
}
`
export const ButtonSecondary = styled(Base)`
background-color: #ebf4ff;
color: #2172e5;
background-color: ${({ theme }) => theme.blue5};
color: ${({ theme }) => theme.blue1};
font-size: 16px;
border-radius: 8px;
padding: 10px;
&:focus {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, '#ebf4ff')};
background-color: ${({ theme }) => darken(0.05, '#ebf4ff')};
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.blue5)};
background-color: ${({ theme }) => darken(0.05, theme.blue5)};
}
&:hover {
background-color: ${({ theme }) => darken(0.05, '#ebf4ff')};
background-color: ${({ theme }) => darken(0.05, theme.blue5)};
}
&:active {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, '#ebf4ff')};
background-color: ${({ theme }) => darken(0.1, '#ebf4ff')};
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.blue5)};
background-color: ${({ theme }) => darken(0.1, theme.blue5)};
}
&:disabled {
background-color: ${({ theme }) => '#ebf4ff'};
background-color: ${({ theme }) => theme.blue5};
opacity: 50%;
cursor: auto;
}
......@@ -113,19 +135,41 @@ export const ButtonPink = styled(Base)`
}
`
export const ButtonEmpty = styled(Base)`
export const ButtonOutlined = styled(Base)`
border: 1px solid #edeef2;
background-color: transparent;
color: black;
color: ${({ theme }) => theme.blue1};
&:focus {
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg3};
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
}
&:hover {
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg3};
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
}
&:active {
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg3};
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
}
&:disabled {
opacity: 50%;
cursor: auto;
}
`
export const ButtonEmpty = styled(Base)`
background-color: transparent;
color: ${({ theme }) => theme.blue1};
display: flex;
justify-content: center;
align-items: center;
&:focus {
background-color: ${({ theme }) => theme.bg3};
}
&:hover {
background-color: ${({ theme }) => theme.bg3};
}
&:active {
background-color: ${({ theme }) => theme.bg3};
}
&:disabled {
opacity: 50%;
......
......@@ -18,7 +18,11 @@ export const LightCard = styled(Card)`
`
export const GreyCard = styled(Card)`
background-color: rgba(255, 255, 255, 0.6);
background-color: rgba(237, 238, 242, 0.5);
`
export const OutlineCard = styled(Card)`
border: 1px solid rgba(237, 238, 242, 0.5);
`
export const YellowCard = styled(Card)`
......
......@@ -19,7 +19,7 @@ const Wrapper = styled.div`
width: 100%;
`
const Section = styled(AutoColumn)`
padding: 26px;
padding: 24px;
`
const BottomSection = styled(Section)`
......
import React, { useState } from 'react'
import React, { useState, useContext } from 'react'
import styled from 'styled-components'
import '@reach/tooltip/styles.css'
import { darken } from 'polished'
......@@ -7,13 +7,14 @@ import TokenLogo from '../TokenLogo'
import DoubleLogo from '../DoubleLogo'
import SearchModal from '../SearchModal'
import { RowBetween } from '../Row'
import { TYPE, Hover } from '../../theme'
import { TYPE } from '../../theme'
import { Input as NumericalInput } from '../NumericalInput'
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
import { useWeb3React } from '../../hooks'
import { useTranslation } from 'react-i18next'
import { useAddressBalance } from '../../contexts/Balances'
import { ThemeContext } from 'styled-components'
const InputRow = styled.div`
${({ theme }) => theme.flexRowNoWrap}
......@@ -49,21 +50,14 @@ const LabelRow = styled.div`
color: ${({ theme }) => theme.text1};
font-size: 0.75rem;
line-height: 1rem;
padding: 0 1.25rem 1rem 1rem;
padding: 0rem 1rem 0.75rem 1rem;
height: 20px
span:hover {
cursor: pointer;
color: ${({ theme }) => darken(0.2, theme.text2)};
}
`
const ErrorSpan = styled.span`
color: ${({ error, theme }) => error && theme.red1};
:hover {
cursor: pointer;
color: ${({ error, theme }) => error && darken(0.1, theme.red1)};
}
`
const Aligner = styled.span`
display: flex;
align-items: center;
......@@ -101,7 +95,7 @@ const StyledTokenName = styled.span`
`
const StyledBalanceMax = styled.button`
height: 35px;
height: 32px;
background-color: ${({ theme }) => theme.blue5};
border: 1px solid ${({ theme }) => theme.blue5};
border-radius: 0.5rem;
......@@ -118,6 +112,26 @@ const StyledBalanceMax = styled.button`
outline: none;
}
`
const StyledBalanceMaxMini = styled.button`
height: 24px;
background-color: ${({ theme, active }) => (active ? theme.blue5 : theme.bg2)};
border: 1px solid ${({ theme }) => theme.blue5};
border-radius: 0.5rem;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
pointer-events: ${({ active }) => (active ? 'initial' : 'none')};
color: ${({ theme, active }) => (active ? theme.blue1 : theme.text4)};
:hover {
border: 1px solid ${({ theme, active }) => (active ? theme.bg2 : theme.bg1)};
}
:focus {
border: 1px solid ${({ theme, active }) => (active ? theme.bg2 : theme.bg1)};
outline: none;
}
`
export default function CurrencyInputPanel({
value,
......@@ -136,13 +150,15 @@ export default function CurrencyInputPanel({
pair = null, // used for double token logo
hideInput = false,
showSendWithSwap = false,
otherSelectedTokenAddress = null
otherSelectedTokenAddress = null,
simplified = false
}) {
const { t } = useTranslation()
const [modalOpen, setModalOpen] = useState(false)
const { account } = useWeb3React()
const userTokenBalance = useAddressBalance(account, token)
const theme = useContext(ThemeContext)
return (
<InputPanel>
......@@ -156,7 +172,7 @@ export default function CurrencyInputPanel({
onUserInput(field, val)
}}
/>
{!!token?.address && !atMax && type !== 'OUTPUT' && (
{!simplified && !!token?.address && !atMax && type !== 'OUTPUT' && (
<StyledBalanceMax onClick={onMax}>MAX</StyledBalanceMax>
)}
</>
......@@ -189,13 +205,27 @@ export default function CurrencyInputPanel({
</Aligner>
</CurrencySelect>
</InputRow>
{!hideBalance && !!token && (
{simplified && !hideBalance && !!token && (
<LabelRow>
<RowBetween>
<ErrorSpan data-tip={'Enter max'} error={!!error} onClick={() => {}}></ErrorSpan>
<Hover onClick={onMax}>
<TYPE.body fontWeight={500}>Balance: {userTokenBalance?.toSignificant(6)}</TYPE.body>
</Hover>
<RowBetween onClick={onMax}>
{/* {!!token?.address && !atMax && type !== 'OUTPUT' ? (
<StyledBalanceMaxMini>Max</StyledBalanceMaxMini>
) : (
<TYPE.body color={'#888D9B'}>Balance</TYPE.body>
)} */}
{!!token?.address &&
(type !== 'OUTPUT' ? (
<StyledBalanceMaxMini active={!atMax}>Max</StyledBalanceMaxMini>
) : (
<TYPE.body color={theme.text3}>-</TYPE.body>
))}
<div>
<TYPE.body color={theme.text2} fontWeight={500} fontSize={14} style={{ display: 'inline' }}>
Balance: {account ? userTokenBalance?.toSignificant(6) : 'Connect to see balances'}
</TYPE.body>
</div>
{/* <ErrorSpan data-tip={'Enter max'} error={!!error} onClick={() => {}}></ErrorSpan> */}
</RowBetween>
</LabelRow>
)}
......
This diff is collapsed.
......@@ -9,22 +9,27 @@ import { Link } from '../../theme'
import { Text } from 'rebass'
import { WETH } from '@uniswap/sdk'
import { isMobile } from 'react-device-detect'
import { YellowCard } from '../Card'
import { YellowCard, GreyCard } from '../Card'
import { useWeb3React } from '../../hooks'
import { useAddressBalance } from '../../contexts/Balances'
import { useWalletModalToggle } from '../../contexts/Application'
import { useUserAdvanced, useToggleUserAdvanced } from '../../contexts/Application'
import { Eye, EyeOff, Send } from 'react-feather'
import { ButtonSecondary } from '../Button'
import Logo from '../../assets/svg/logo.svg'
import Wordmark from '../../assets/svg/wordmark.svg'
import { AutoColumn } from '../Column'
const HeaderFrame = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
position: absolute;
padding: 0 10px;
width: calc(100% - 20px);
/* position: absolute; */
padding: 1rem 1rem;
width: calc(100% - 2rem);
${({ theme }) => theme.mediaWidth.upToExtraSmall`
padding: 10px;
......@@ -89,49 +94,127 @@ const NetworkCard = styled(YellowCard)`
border-radius: 12px;
padding: 8px 12px;
`
const Alpha = styled(GreyCard)`
width: fit-content;
margin-right: 10px;
border-radius: 12px;
padding: 8px 12px;
color: ${({ theme }) => theme.text2};
`
const UniIcon = styled(Link)`
transition: transform 0.3s ease;
:hover {
transform: rotate(-5deg);
}
`
const StyledRed = styled.div`
width: 100%;
height: 150vh;
border-radius: 10vw;
background: ${({ theme }) => `radial-gradient(50% 50% at 50% 50%, ${theme.pink2} 0%, ${theme.white} 100%)`};
position: absolute;
top: 0px;
left: 0px;
opacity: 0.1;
z-index: -1;
transform: translateY(-70vh);
@media (max-width: 960px) {
height: 300px;
width: 100%;
transform: translateY(-150px);
}
`
const MigrateBanner = styled(AutoColumn)`
width: 100%;
padding: 12px 0;
display: flex;
justify-content: center;
background-color: rgba(255, 0, 122, 0.1);
color: ${({ theme }) => theme.pink2};
font-weight: 500;
text-align: center;
a {
color: ${({ theme }) => theme.pink2};
}
`
export default function Header() {
const { account, chainId } = useWeb3React()
const userEthBalance = useAddressBalance(account, WETH[chainId])
const toggleWalletModal = useWalletModalToggle()
const toggleSimplified = useToggleUserAdvanced()
const advanced = useUserAdvanced()
return (
<HeaderFrame>
<HeaderElement>
<Title>
<Link id="link" href="https://uniswap.io">
<img src={Logo} alt="logo" />
</Link>
{!isMobile && (
<TitleText>
<Link id="link" href="https://uniswap.io">
<img style={{ marginLeft: '4px' }} src={Wordmark} alt="logo" />
</Link>
<p style={{ opacity: 0.6, marginLeft: '4px', fontSize: '16px' }}>{'/ Exchange'}</p>
</TitleText>
)}
</Title>
</HeaderElement>
<HeaderElement>
<TestnetWrapper>
{!isMobile && chainId === 4 && <NetworkCard>Rinkeby Testnet</NetworkCard>}
{!isMobile && chainId === 3 && <NetworkCard> Ropsten Testnet</NetworkCard>}
{!isMobile && chainId === 5 && <NetworkCard>Goerli Testnet</NetworkCard>}
{!isMobile && chainId === 42 && <NetworkCard>Kovan Testnet</NetworkCard>}
</TestnetWrapper>
<AccountElement active={!!account}>
{account ? (
<Row style={{ marginRight: '-1.25rem', paddingRight: '1.75rem' }}>
<Text fontWeight={400}> {userEthBalance && userEthBalance?.toFixed(4) + ' ETH'}</Text>
</Row>
) : (
''
)}
<Web3Status onClick={toggleWalletModal} />
</AccountElement>
<Menu />
</HeaderElement>
</HeaderFrame>
<AutoColumn style={{ width: '100vw' }}>
<MigrateBanner>
<StyledRed />
<b>Uniswap V2 is live.&nbsp;</b> Move your liquidity now using the&nbsp;
<a href="https://migrate.uniswap.exchange/">migration helper</a>&nbsp; or read the&nbsp;
<a href="https://uniswap.org/blog/uniswap-v2/">announcement </a>
</MigrateBanner>
<HeaderFrame>
<HeaderElement>
<Title>
<UniIcon id="link" href="/">
<img src={Logo} alt="logo" />
</UniIcon>
{!isMobile && (
<TitleText>
<Link id="link" href="/">
<img style={{ marginLeft: '4px', marginTop: '4px' }} src={Wordmark} alt="logo" />
</Link>
</TitleText>
)}
<TestnetWrapper>{!isMobile && <Alpha>1.0.0-alpha</Alpha>}</TestnetWrapper>
</Title>
</HeaderElement>
<HeaderElement>
<ButtonSecondary
style={{
padding: ' 8px 12px',
marginRight: '0px',
width: 'fit-content',
position: 'fixed',
right: '1rem',
bottom: '1rem'
}}
>
<Send size={16} style={{ marginRight: '8px' }} /> Feeback
</ButtonSecondary>
<ButtonSecondary
style={{ padding: '6px 8px', marginRight: '0px', width: 'fit-content' }}
onClick={toggleSimplified}
>
{' '}
{advanced ? <EyeOff size={20} /> : <Eye size={20} />}
</ButtonSecondary>
<TestnetWrapper>
{!isMobile && chainId === 4 && <NetworkCard>Rinkeby</NetworkCard>}
{!isMobile && chainId === 3 && <NetworkCard>Ropsten</NetworkCard>}
{!isMobile && chainId === 5 && <NetworkCard>Goerli</NetworkCard>}
{!isMobile && chainId === 42 && <NetworkCard>Kovan</NetworkCard>}
</TestnetWrapper>
<AccountElement active={!!account}>
{account ? (
<Row style={{ marginRight: '-1.25rem', paddingRight: '1.75rem' }}>
<Text fontWeight={400}> {userEthBalance && userEthBalance?.toFixed(4) + ' ETH'}</Text>
</Row>
) : (
''
)}
<Web3Status onClick={toggleWalletModal} />
</AccountElement>
<Menu />
</HeaderElement>
</HeaderFrame>
</AutoColumn>
)
}
......@@ -54,7 +54,7 @@ const StyledDialogContent = styled(FilteredDialogContent)`
padding: 0px;
width: 50vw;
max-width: 500px;
max-width: 355px;
${({ maxHeight }) =>
maxHeight &&
css`
......@@ -67,7 +67,7 @@ const StyledDialogContent = styled(FilteredDialogContent)`
`}
display: flex;
/* overflow: hidden; */
border-radius: 10px;
border-radius: 20px;
${({ theme }) => theme.mediaWidth.upToMedium`
width: 65vw;
max-height: 65vh;
......
......@@ -15,6 +15,9 @@ const StyledInput = styled.input`
background-color: ${({ theme }) => theme.bg1};
font-size: ${({ fontSize }) => fontSize && fontSize};
text-align: ${({ align }) => align && align};
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
[type='number'] {
-moz-appearance: textfield;
......
......@@ -3,14 +3,8 @@ import styled from 'styled-components'
import { useMediaLayout } from 'use-media'
import { X } from 'react-feather'
import { Text } from 'rebass'
import { TYPE } from '../../theme'
import { Hover } from '../../theme/components'
import { PinkCard } from '../Card'
import { ButtonPink } from '../Button'
import { usePopups } from '../../contexts/Application'
import { AutoColumn } from '../Column'
import { useMigrationMessageManager } from '../../contexts/LocalStorage'
const StyledClose = styled(X)`
position: absolute;
......@@ -25,9 +19,9 @@ const StyledClose = styled(X)`
const MobilePopupWrapper = styled.div`
position: relative;
max-width: 100%;
height: ${({ height }) => height}
margin: ${({ height }) => (height ? '0 auto;' : 0)}
margin-bottom: ${({ height }) => (height ? '20px' : 0)}}
height: ${({ height }) => height};
margin: ${({ height }) => (height ? '0 auto;' : 0)};
margin-bottom: ${({ height }) => (height ? '20px' : 0)}};
`
const MobilePopupInner = styled.div`
......@@ -45,8 +39,8 @@ const MobilePopupInner = styled.div`
const FixedPopupColumn = styled(AutoColumn)`
position: absolute;
top: 56px;
right: 24px;
top: 72px;
right: 1rem;
width: 355px;
${({ theme }) => theme.mediaWidth.upToSmall`
......@@ -60,7 +54,7 @@ const Popup = styled.div`
padding: 1em;
box-sizing: border-box;
background-color: white;
margin: 0 10px;
/* margin: 0 10px; */
position: relative;
border-radius: 10px;
padding: 20px;
......@@ -73,19 +67,10 @@ const Popup = styled.div`
`}
`
const MobileCardPink = styled(PinkCard)`
z-index: 2;
padding: 20px;
white-space: normal;
`
export default function App() {
// get all popups
const [activePopups, , removePopup] = usePopups()
// local storage reference to show message
const [showMessage, hideMigrationMessage] = useMigrationMessageManager()
// switch view settings on mobile
const isMobile = useMediaLayout({ maxWidth: '600px' })
......@@ -100,25 +85,13 @@ export default function App() {
</Popup>
)
})}
{showMessage && (
<PinkCard padding="20px" style={{ zIndex: '2' }}>
<AutoColumn justify={'center'} gap={'20px'}>
<Hover onClick={() => hideMigrationMessage()}>
<StyledClose />
</Hover>
<TYPE.largeHeader>Uniswap has upgraded.</TYPE.largeHeader>
<Text textAlign="center">Are you a liquidity provider? Upgrade now using the migration helper.</Text>
<ButtonPink width={'265px'}>Migrate your liquidity </ButtonPink>
</AutoColumn>
</PinkCard>
)}
</FixedPopupColumn>
)
}
//mobile
else
return (
<MobilePopupWrapper height={activePopups?.length > 0 || showMessage ? 'fit-content' : 0}>
<MobilePopupWrapper height={activePopups?.length > 0 ? 'fit-content' : 0}>
<MobilePopupInner>
{activePopups // reverse so new items up front
.slice(0)
......@@ -131,18 +104,6 @@ export default function App() {
</Popup>
)
})}
{showMessage && (
<MobileCardPink>
<AutoColumn justify={'center'} gap={'20px'}>
<Hover onClick={() => hideMigrationMessage()}>
<StyledClose />
</Hover>
<Text>Uniswap has upgraded.</Text>
<Text textAlign="center">Are you a liquidity provider? Upgrade now using the migration helper.</Text>
<ButtonPink width={'265px'}>Migrate your liquidity </ButtonPink>
</AutoColumn>
</MobileCardPink>
)}
</MobilePopupInner>
</MobilePopupWrapper>
)
......
......@@ -26,7 +26,6 @@ const FixedHeightRow = styled(RowBetween)`
const HoverCard = styled(Card)`
border: 1px solid ${({ theme }) => theme.bg3};
:hover {
cursor: pointer;
border: 1px solid ${({ theme }) => darken(0.06, theme.bg3)};
}
`
......@@ -66,68 +65,72 @@ function PositionCard({ pairAddress, token0, token1, history, minimal = false, .
if (minimal) {
return (
<GreyCard {...rest}>
<AutoColumn gap="12px">
<FixedHeightRow>
<RowFixed>
<Text fontWeight={500} fontSize={16}>
Your current position
</Text>
</RowFixed>
</FixedHeightRow>
<FixedHeightRow onClick={() => setShowMore(!showMore)}>
<RowFixed>
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={20} />
<Text fontWeight={500} fontSize={20}>
{token0?.symbol}:{token1?.symbol}
</Text>
</RowFixed>
<RowFixed>
<Text fontWeight={500} fontSize={20}>
{userPoolBalance ? userPoolBalance.toFixed(6) : '-'}
</Text>
</RowFixed>
</FixedHeightRow>
<AutoColumn gap="4px">
<FixedHeightRow>
<Text color="#888D9B" fontSize={14} fontWeight={500}>
{token0?.symbol}:
</Text>
{token0Deposited ? (
<>
{userPoolBalance && userPoolBalance.toFixed(6) > 0 && (
<GreyCard {...rest}>
<AutoColumn gap="12px">
<FixedHeightRow>
<RowFixed>
{!minimal && <TokenLogo address={token0?.address || ''} />}
<Text color="#888D9B" fontSize={14} fontWeight={500} marginLeft={'6px'}>
{token0Deposited?.toFixed(8)}
<Text fontWeight={500} fontSize={16}>
Your current position
</Text>
</RowFixed>
) : (
'-'
)}
</FixedHeightRow>
<FixedHeightRow>
<Text color="#888D9B" fontSize={14} fontWeight={500}>
{token1?.symbol}:
</Text>
{token1Deposited ? (
</FixedHeightRow>
<FixedHeightRow onClick={() => setShowMore(!showMore)}>
<RowFixed>
{!minimal && <TokenLogo address={token1?.address || ''} />}
<Text color="#888D9B" fontSize={14} fontWeight={500} marginLeft={'6px'}>
{token1Deposited?.toFixed(8)}
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={20} />
<Text fontWeight={500} fontSize={20}>
{token0?.symbol}:{token1?.symbol}
</Text>
</RowFixed>
) : (
'-'
)}
</FixedHeightRow>
</AutoColumn>
</AutoColumn>
</GreyCard>
<RowFixed>
<Text fontWeight={500} fontSize={20}>
{userPoolBalance ? userPoolBalance.toFixed(6) : '-'}
</Text>
</RowFixed>
</FixedHeightRow>
<AutoColumn gap="4px">
<FixedHeightRow>
<Text color="#888D9B" fontSize={16} fontWeight={500}>
{token0?.symbol}:
</Text>
{token0Deposited ? (
<RowFixed>
{!minimal && <TokenLogo address={token0?.address || ''} />}
<Text color="#888D9B" fontSize={16} fontWeight={500} marginLeft={'6px'}>
{token0Deposited?.toFixed(8)}
</Text>
</RowFixed>
) : (
'-'
)}
</FixedHeightRow>
<FixedHeightRow>
<Text color="#888D9B" fontSize={16} fontWeight={500}>
{token1?.symbol}:
</Text>
{token1Deposited ? (
<RowFixed>
{!minimal && <TokenLogo address={token1?.address || ''} />}
<Text color="#888D9B" fontSize={16} fontWeight={500} marginLeft={'6px'}>
{token1Deposited?.toFixed(8)}
</Text>
</RowFixed>
) : (
'-'
)}
</FixedHeightRow>
</AutoColumn>
</AutoColumn>
</GreyCard>
)}
</>
)
} else
return (
<HoverCard {...rest} onClick={() => setShowMore(!showMore)}>
<HoverCard {...rest}>
<AutoColumn gap="12px">
<FixedHeightRow>
<FixedHeightRow onClick={() => setShowMore(!showMore)} style={{ cursor: 'pointer' }}>
<RowFixed>
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={20} />
<Text fontWeight={500} fontSize={20}>
......@@ -146,17 +149,17 @@ function PositionCard({ pairAddress, token0, token1, history, minimal = false, .
</RowFixed>
</FixedHeightRow>
{showMore && (
<AutoColumn gap="4px">
<AutoColumn gap="8px">
<FixedHeightRow>
<RowFixed>
{!minimal && <TokenLogo size="16px" style={{ marginRight: '4px' }} address={token0?.address || ''} />}
<Text color="#888D9B" fontSize={14} fontWeight={500}>
Your {token0?.symbol}:
{!minimal && <TokenLogo size="20px" style={{ marginRight: '8px' }} address={token0?.address || ''} />}
<Text color="#888D9B" fontSize={16} fontWeight={500}>
Pooled {token0?.symbol}:
</Text>
</RowFixed>
{token0Deposited ? (
<RowFixed>
<Text color="#888D9B" fontSize={14} fontWeight={500} marginLeft={'6px'}>
<Text color="#888D9B" fontSize={16} fontWeight={500} marginLeft={'6px'}>
{token0Deposited?.toFixed(8)}
</Text>
</RowFixed>
......@@ -164,15 +167,16 @@ function PositionCard({ pairAddress, token0, token1, history, minimal = false, .
'-'
)}
</FixedHeightRow>
<FixedHeightRow>
<RowFixed>
{!minimal && <TokenLogo size="16px" style={{ marginRight: '4px' }} address={token1?.address || ''} />}
<Text color="#888D9B" fontSize={14} fontWeight={500}>
Your {token1?.symbol}:
{!minimal && <TokenLogo size="20px" style={{ marginRight: '8px' }} address={token1?.address || ''} />}
<Text color="#888D9B" fontSize={16} fontWeight={500}>
Pooled {token1?.symbol}:
</Text>
</RowFixed>
{token1Deposited ? (
<Text color="#888D9B" fontSize={14} fontWeight={500} marginLeft={'6px'}>
<Text color="#888D9B" fontSize={16} fontWeight={500} marginLeft={'6px'}>
{token1Deposited?.toFixed(8)}
</Text>
) : (
......@@ -181,14 +185,18 @@ function PositionCard({ pairAddress, token0, token1, history, minimal = false, .
</FixedHeightRow>
{!minimal && (
<FixedHeightRow>
<Text color="#888D9B" fontSize={14} fontWeight={500}>
<Text color="#888D9B" fontSize={16} fontWeight={500}>
Your pool share:
</Text>
<Text color="#888D9B" fontSize={14} fontWeight={500}>
<Text color="#888D9B" fontSize={16} fontWeight={500}>
{poolTokenPercentage ? poolTokenPercentage.toFixed(2) + '%' : '-'}
</Text>
</FixedHeightRow>
)}
<AutoRow justify="center" marginTop={'10px'}>
<Link>View pool information </Link>
</AutoRow>
<RowBetween marginTop="10px">
<ButtonSecondary
width="48%"
......@@ -207,9 +215,6 @@ function PositionCard({ pairAddress, token0, token1, history, minimal = false, .
Remove
</ButtonSecondary>
</RowBetween>
<AutoRow justify="center" marginTop={'10px'}>
<Link>View analytics</Link>
</AutoRow>
</AutoColumn>
)}
</AutoColumn>
......
......@@ -18,6 +18,7 @@ import { Hover } from '../../theme'
import { ArrowLeft, X } from 'react-feather'
import { CloseIcon } from '../../theme/components'
import { ColumnCenter } from '../../components/Column'
import Card from '../../components/Card'
import { Spinner, TYPE } from '../../theme'
import { RowBetween, RowFixed, AutoRow } from '../Row'
......@@ -48,8 +49,7 @@ const TokenList = styled.div`
const FadedSpan = styled.span`
color: ${({ theme }) => theme.blue1};
display: flex;
align-items: center;
font-size: 14px;
`
const GreySpan = styled.span`
......@@ -99,12 +99,12 @@ const FilterWrapper = styled(RowFixed)`
`
const PaddedColumn = styled(AutoColumn)`
padding: 24px;
padding: 20px;
padding-bottom: 12px;
`
const PaddedItem = styled(RowBetween)`
padding: 4px 24px;
padding: 4px 20px;
height: 56px;
`
......@@ -519,8 +519,7 @@ function SearchModal({
<Modal
isOpen={isOpen}
onDismiss={clearInputAndDismiss}
minHeight={60}
maxHeight={50}
maxHeight={70}
initialFocusRef={isMobile ? undefined : inputRef}
>
<Column style={{ width: '100%' }}>
......@@ -541,7 +540,7 @@ function SearchModal({
</RowFixed>
<CloseIcon onClick={onDismiss} />
</RowBetween>
<TYPE.body style={{ marginTop: '40px' }}>
<TYPE.body style={{ marginTop: '10px' }}>
To import a custom token, paste token address in the search bar.
</TYPE.body>
<Input
......@@ -596,10 +595,22 @@ function SearchModal({
</AutoColumn>
)}
<RowBetween>
<Text fontSize={14} fontWeight={500}>
Token Name
</Text>
<Filter title="Your Balances" filter={FILTERS.BALANCES} />
</RowBetween>
</PaddedColumn>
)}
{!showTokenImport && <div style={{ width: '100%', height: '1px', backgroundColor: '#E1E1E1' }} />}
{!showTokenImport && <TokenList>{filterType === 'tokens' ? renderTokenList() : renderPairsList()}</TokenList>}
{!showTokenImport && (
<Card>
<AutoRow justify={'center'}>
<div>
{filterType !== 'tokens' && (
<Text fontWeight={500}>
{!isMobile && 'Dont see a pool? '}
{!isMobile && "Don't see a pool? "}
<StyledLink
onClick={() => {
history.push('/find')
......@@ -611,7 +622,8 @@ function SearchModal({
)}
{filterType === 'tokens' && (
<Text fontWeight={500}>
{!isMobile && 'Dont see a token? '}
{!isMobile && "Don't see a token? "}
<StyledLink
onClick={() => {
setShowTokenImport(true)
......@@ -622,13 +634,9 @@ function SearchModal({
</Text>
)}
</div>
<div />
<Filter title="Your Balances" filter={FILTERS.BALANCES} />
</RowBetween>
</PaddedColumn>
</AutoRow>
</Card>
)}
{!showTokenImport && <div style={{ width: '100%', height: '1px', backgroundColor: '#E1E1E1' }} />}
{!showTokenImport && <TokenList>{filterType === 'tokens' ? renderTokenList() : renderPairsList()}</TokenList>}
</Column>
</Modal>
)
......
......@@ -51,7 +51,13 @@ export default function TokenLogo({ address, size = '24px', ...rest }) {
let path = ''
if (address === 'ETH') {
return <StyledEthereumLogo size={size} {...rest} />
return (
<StyledEthereumLogo
style={{ boxShadow: '0px 6px 10px rgba(0, 0, 0, 0.075)', borderRadius: '24px' }}
size={size}
{...rest}
/>
)
} else if (!error && !BAD_IMAGES[address]) {
path = TOKEN_ICON_API(address?.toLowerCase())
} else {
......
......@@ -12,7 +12,7 @@ const InfoCard = styled.button`
&:focus {
box-shadow: 0 0 0 1px ${({ theme }) => theme.blue1};
}
border-color: ${({ theme, active }) => (active ? 'transparent' : theme.bg4)};
border-color: ${({ theme, active }) => (active ? 'transparent' : theme.bg3)};
`
const OptionCard = styled(InfoCard)`
......@@ -34,7 +34,7 @@ const OptionCardClickable = styled(OptionCard)`
margin-top: 0;
&:hover {
cursor: ${({ clickable }) => (clickable ? 'pointer' : '')};
border: ${({ clickable, theme }) => (clickable ? `1px solid ${theme.blue2}` : ``)};
border: ${({ clickable, theme }) => (clickable ? `1px solid ${theme.blue1}` : ``)};
}
opacity: ${({ disabled }) => (disabled ? '0.5' : '1')};
`
......
......@@ -84,6 +84,7 @@ const Blurb = styled.div`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
justify-content: center;
flex-wrap: wrap;
margin-top: 2rem;
${({ theme }) => theme.mediaWidth.upToMedium`
margin: 1rem;
......@@ -93,7 +94,7 @@ const Blurb = styled.div`
const OptionGrid = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
/* grid-template-columns: 1fr 1fr; */
grid-gap: 10px;
${({ theme }) => theme.mediaWidth.upToMedium`
grid-template-columns: 1fr;
......
......@@ -13,6 +13,8 @@ const UPDATE_BLOCK_NUMBER = 'UPDATE_BLOCK_NUMBER'
const TOGGLE_WALLET_MODAL = 'TOGGLE_WALLET_MODAL'
const ADD_POPUP = 'ADD_POPUP'
const USER_ADVANCED = 'USER_ADVANCED'
const TOGGLE_USER_ADVANCED = 'TOGGLE_USER_ADVANCED'
const ApplicationContext = createContext()
......@@ -37,6 +39,10 @@ function reducer(state, { type, payload }) {
return { ...state, [WALLET_MODAL_OPEN]: !state[WALLET_MODAL_OPEN] }
}
case TOGGLE_USER_ADVANCED: {
return { ...state, [USER_ADVANCED]: !state[USER_ADVANCED] }
}
case ADD_POPUP: {
const { newList } = payload
return { ...state, [POPUP_LIST]: newList, [POPUP_KEY]: state?.[POPUP_KEY] + 1 }
......@@ -54,7 +60,8 @@ export default function Provider({ children }) {
[USD_PRICE]: {},
[POPUP_LIST]: [],
[POPUP_KEY]: 0,
[WALLET_MODAL_OPEN]: false
[WALLET_MODAL_OPEN]: false,
[USER_ADVANCED]: true
})
const updateBlockNumber = useCallback((networkId, blockNumber) => {
......@@ -65,16 +72,21 @@ export default function Provider({ children }) {
dispatch({ type: TOGGLE_WALLET_MODAL })
}, [])
const toggleUserAdvanced = useCallback(() => {
dispatch({ type: TOGGLE_USER_ADVANCED })
}, [])
const setPopups = useCallback(newList => {
dispatch({ type: ADD_POPUP, payload: { newList } })
}, [])
return (
<ApplicationContext.Provider
value={useMemo(() => [state, { updateBlockNumber, toggleWalletModal, setPopups }], [
value={useMemo(() => [state, { updateBlockNumber, toggleWalletModal, toggleUserAdvanced, setPopups }], [
state,
updateBlockNumber,
toggleWalletModal,
toggleUserAdvanced,
setPopups
])}
>
......@@ -141,6 +153,17 @@ export function useWalletModalToggle() {
return toggleWalletModal
}
export function useUserAdvanced() {
const [state] = useApplicationContext()
return state[USER_ADVANCED]
}
export function useToggleUserAdvanced() {
const [, { toggleUserAdvanced }] = useApplicationContext()
return toggleUserAdvanced
}
export function usePopups() {
const [state, { setPopups }] = useApplicationContext()
......
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
import { ChainId, WETH, Token } from '@uniswap/sdk'
import { useWeb3React } from '../hooks'
import { isAddress, getTokenName, getTokenSymbol, getTokenDecimals, safeAccess } from '../utils'
const UPDATE = 'UPDATE'
export let ALL_TOKENS = [
//Mainnet Tokens
WETH[ChainId.MAINNET],
// Rinkeby Tokens
WETH[ChainId.RINKEBY],
new Token(ChainId.RINKEBY, '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735', 18, 'DAI', 'Dai Stablecoin'),
new Token(ChainId.RINKEBY, '0x8ab15C890E5C03B5F240f2D146e3DF54bEf3Df44', 18, 'IANV2', 'IAn V2 /Coin'),
// new Token(ChainId.RINKEBY, '0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85', 18, 'MKR', 'Maker'),
//Kovan Tokens
WETH[ChainId.KOVAN],
new Token(ChainId.KOVAN, '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa', 18, 'DAI', 'Dai Stablecoin'),
//Ropsten Tokens
WETH[ChainId.ROPSTEN],
new Token(ChainId.ROPSTEN, '0xaD6D458402F60fD3Bd25163575031ACDce07538D', 18, 'DAI', 'Dai Stablecoin'),
//Goerli Tokens
WETH[ChainId.GÖRLI]
]
/**
* @todo is there a better way to load these upfront?
*/
// add any tokens from local storage
const savedList = window?.localStorage?.UNISWAP && JSON.parse(window?.localStorage?.UNISWAP)?.TOKEN_LIST
if (savedList) {
const newTokens = Object.keys(savedList).map(key => {
const token = savedList[key]
return new Token(token.chainId, token.address, token.decimals, token.symbol, token.name)
})
ALL_TOKENS = ALL_TOKENS.concat(newTokens)
}
// only meant to be used in exchanges.ts!
export const INITIAL_TOKENS_CONTEXT = ALL_TOKENS.reduce((tokenMap, token) => {
// ensure tokens are unique
if (tokenMap?.[token.chainId]?.[token.address] !== undefined) throw Error(`Duplicate token: ${token}`)
return {
...tokenMap,
[token.chainId]: {
...tokenMap?.[token.chainId],
[token.address]: token
}
}
}, {})
const TokensContext = createContext([])
function useTokensContext() {
return useContext(TokensContext)
}
function reducer(state, { type, payload }) {
switch (type) {
case UPDATE: {
const { chainId, token } = payload
return {
...state,
[chainId]: {
...(state?.[chainId] || {}),
[token.address]: token
}
}
}
default: {
throw Error(`Unexpected action type in TokensContext reducer: '${type}'.`)
}
}
}
export default function Provider({ children }) {
const [state, dispatch] = useReducer(reducer, INITIAL_TOKENS_CONTEXT)
const update = useCallback((chainId, token) => {
dispatch({ type: UPDATE, payload: { chainId, token } })
}, [])
return (
<TokensContext.Provider value={useMemo(() => [state, { update }], [state, update])}>
{children}
</TokensContext.Provider>
)
}
export function useToken(tokenAddress: string): Token {
const { library, chainId } = useWeb3React()
const [state, { update }] = useTokensContext()
const allTokensInNetwork = state?.[chainId] || {}
const token = safeAccess(allTokensInNetwork, [tokenAddress])
useEffect(() => {
if (
isAddress(tokenAddress) &&
(token === null || token.name === undefined || token.symbol === undefined || token.decimals === undefined) &&
(chainId || chainId === 0) &&
library
) {
let stale = false
const namePromise = getTokenName(tokenAddress, library).catch(() => null)
const symbolPromise = getTokenSymbol(tokenAddress, library).catch(() => null)
const decimalsPromise = getTokenDecimals(tokenAddress, library).catch(() => null)
Promise.all([namePromise, symbolPromise, decimalsPromise]).then(
([resolvedName, resolvedSymbol, resolvedDecimals]) => {
if (!stale && resolvedDecimals) {
const newToken: Token = new Token(chainId, tokenAddress, resolvedDecimals, resolvedSymbol, resolvedName)
update(chainId, newToken)
}
}
)
return () => {
stale = true
}
}
}, [tokenAddress, token, chainId, library, update])
// hard coded change in UI to display WETH as ETH
if (token && token.name === 'WETH') {
token.name = 'ETH'
}
if (token && token.symbol === 'WETH') {
token.symbol = 'ETH'
}
return token
}
export function useAllTokens(): string[] {
const { chainId } = useWeb3React()
const [state] = useTokensContext()
return useMemo(() => {
// hardcode overide weth as ETH
if (state && state[chainId] && state[chainId][WETH[chainId].address]) {
state[chainId][WETH[chainId].address].symbol = 'ETH'
state[chainId][WETH[chainId].address].name = 'ETH'
}
return state?.[chainId] || {}
}, [state, chainId])
}
......@@ -40,7 +40,7 @@ const BodyWrapper = styled.div`
align-items: center;
flex: 1;
overflow: auto;
padding-top: 100px;
padding-top: 60px;
& > * {
max-width: calc(355px + 4rem);
......
......@@ -2,6 +2,6 @@ import React from 'react'
import ExchangePage from '../../components/ExchangePage'
export default function Send({ initialCurrency, params }) {
return <ExchangePage sendingInput={true} initialCurrency={initialCurrency} params={params} />
export default function Send({ params }) {
return <ExchangePage sendingInput={true} params={params} />
}
......@@ -10,7 +10,7 @@ import { Link, TYPE } from '../../theme'
import { Text } from 'rebass'
import { LightCard } from '../../components/Card'
import { RowBetween } from '../../components/Row'
import { ButtonPrimary } from '../../components/Button'
import { ButtonPrimary, ButtonSecondary } from '../../components/Button'
import { AutoColumn, ColumnCenter } from '../../components/Column'
import { useAllPairs } from '../../contexts/Pairs'
......@@ -70,7 +70,7 @@ function Supply({ history }) {
<Text fontSize={20}>Join {filteredExchangeList?.length > 0 ? 'another' : 'a'} pool</Text>
</ButtonPrimary>
<Positions>
<AutoColumn gap="20px">
<AutoColumn gap="12px">
<RowBetween padding={'0 8px'}>
<Text fontWeight={500}>Your Pooled Liquidity</Text>
<Question text="filler text" />
......@@ -84,7 +84,7 @@ function Supply({ history }) {
</LightCard>
)}
{filteredExchangeList}
<Text color="#AEAEAE" textAlign="center">
<Text color="#AEAEAE" textAlign="center" style={{ padding: '.5rem 0 .5rem 0' }}>
{filteredExchangeList?.length !== 0 ? `Don't see a pool you joined? ` : 'Already joined a pool? '}{' '}
<Link
onClick={() => {
......@@ -97,9 +97,9 @@ function Supply({ history }) {
</AutoColumn>
<FixedBottom>
<ColumnCenter>
<ButtonPrimary width="120px" padding="8px" borderRadius="10px" onClick={() => history.push('/create')}>
Create Pool
</ButtonPrimary>
<ButtonSecondary width="136px" padding="8px" borderRadius="10px" onClick={() => history.push('/create')}>
+ Create Pool
</ButtonSecondary>
</ColumnCenter>
</FixedBottom>
</Positions>
......
......@@ -42,7 +42,7 @@ export default function ThemeProvider({ children }) {
return <StyledComponentsThemeProvider theme={theme(themeToRender)}>{children}</StyledComponentsThemeProvider>
}
const theme = darkMode => ({
export const theme = darkMode => ({
// base
white,
black,
......@@ -62,11 +62,14 @@ const theme = darkMode => ({
bg5: darkMode ? '#565A69' : '#888D9B',
//blues
blue1: '#2172E5',
// blue1: '#2172E5',
blue1: '#ff007a',
blue2: darkMode ? '#3680E7' : '#1966D2',
blue3: darkMode ? '#4D8FEA' : '#165BBB',
blue4: '#C4D9F8',
blue5: '#EBF4FF',
// blue5: '#EBF4FF',
// blue4: '#C4D9F8',
blue4: '#FDEAF1',
blue5: '#FDEAF1',
// pinks
pink1: '#DC6BE5',
......@@ -172,6 +175,11 @@ export const GlobalStyle = createGlobalStyle`
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@531&display=swap');
html {
font-family: 'Inter', sans-serif;
letter-spacing: -0.018em;
/* font-feature-settings: 'cv01', 'cv02', 'cv03', 'cv04'; */
}
@supports (font-variation-settings: normal) {
html { font-family: 'Inter var', sans-serif; }
}
html,
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment