Skip to content

Commit

Permalink
add earn banner (#2572)
Browse files Browse the repository at this point in the history
* add earn banner

* fix banner layout for large screen

* change open pool to landing page

* remove (s) from earn url

* add back button to earn page

* fix data change interval

* add redirect for earn url
  • Loading branch information
tienkane authored Jan 8, 2025
1 parent fb92fc1 commit b0b9e75
Show file tree
Hide file tree
Showing 10 changed files with 289 additions and 14 deletions.
Binary file added src/assets/images/earn_background_large.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/earn_background_small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
237 changes: 237 additions & 0 deletions src/components/EarnBanner/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
import { useEffect, useMemo, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { Flex, Text } from 'rebass'
import { useExplorerLandingQuery } from 'services/zapEarn'
import styled, { css, keyframes } from 'styled-components'

import earnLargeBg from 'assets/images/earn_background_large.png'
import earnSmallBg from 'assets/images/earn_background_small.png'
import { APP_PATHS } from 'constants/index'
import { useActiveWeb3React } from 'hooks'
import { formatAprNumber } from 'pages/Earns/utils'

const EarnBannerContainer = styled.div`
padding: 1px;
position: relative;
background-clip: padding-box;
overflow: hidden;
margin-bottom: 20px;
border-radius: 12px;
border: 1px solid transparent;
cursor: pointer;
::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 1px;
background: linear-gradient(306.9deg, #262525 38.35%, rgba(148, 117, 203, 0.2) 104.02%),
radial-gradient(58.61% 54.58% at 30.56% 0%, rgba(130, 71, 229, 0.6) 0%, rgba(130, 71, 229, 0) 100%);
mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
-webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
z-index: -1;
}
`

const EarnBannerWrapper = styled.div`
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 22px 18px 22px 68px;
background-image: url(${earnLargeBg});
background-position: center;
background-size: cover;
${({ theme }) => theme.mediaWidth.upToXL`
padding: 20px 18px;
`}
${({ theme }) => theme.mediaWidth.upToLarge`
flex-direction: column;
background-image: url(${earnSmallBg});
padding: 20px 24px;
`}
${({ theme }) => theme.mediaWidth.upToMedium`
flex-direction: row;
background-image: url(${earnLargeBg});
padding: 20px 18px 20px 60px;
`}
@media screen and (max-width: 900px) {
padding: 20px 18px;
}
${({ theme }) => theme.mediaWidth.upToSmall`
flex-direction: column;
background-image: url(${earnSmallBg});
padding: 20px 24px;
`}
`

const Description = styled(Text)`
width: 400px;
${({ theme }) => theme.mediaWidth.upToXL`
width: unset;
`}
`

const PrimaryText = styled.span`
color: ${({ theme }) => theme.primary};
`

const pulse = keyframes`
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
`

const PoolButton = styled.div<{ animate: boolean }>`
border-radius: 40px;
padding: 10px 20px;
background: #1d5b49cc;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
border: 1px solid rgba(25, 103, 80, 1);
${({ animate }) =>
animate &&
css`
animation: ${pulse} 0.6s;
`}
${({ theme }) => theme.mediaWidth.upToLarge`
width: 100%;
`}
${({ theme }) => theme.mediaWidth.upToMedium`
width: unset;
`}
${({ theme }) => theme.mediaWidth.upToSmall`
width: 100%;
padding: 10px 16px;
`}
${({ theme }) => theme.mediaWidth.upToExtraSmall`
padding: 10px 12px;
`}
`

const TokenImage = styled.img`
width: 24px;
height: 24px;
border-radius: 50%;
box-shadow: 0 4px 8px 0 rgba(11, 46, 36, 1);
&:nth-child(1) {
margin-right: -8px;
}
`

const PoolAprWrapper = styled.div`
border-radius: 20px;
box-shadow: 0 8px 8px 0 rgba(0, 0, 0, 0.3);
padding-bottom: 1px;
width: auto;
overflow: hidden;
background-image: linear-gradient(
to right,
rgba(102, 102, 102, 0),
rgba(102, 102, 102, 0),
rgba(162, 233, 212, 1),
rgba(102, 102, 102, 0),
rgba(102, 102, 102, 0)
);
`

const PoolApr = styled.div`
display: flex;
font-weight: 600;
background-color: #000;
color: ${({ theme }) => theme.primary};
padding: 4px 16px;
width: max-content;
`

const AprText = styled.span`
margin-left: 6px;
${({ theme }) => theme.mediaWidth.upToXXSmall`
display: none;
`}
`

let indexInterval: NodeJS.Timeout

export default function EarnBanner() {
const navigate = useNavigate()
const { account } = useActiveWeb3React()
const { data } = useExplorerLandingQuery({ userAddress: account })

const [index, setIndex] = useState(0)
const [animate, setAnimate] = useState(false)

const pool = useMemo(() => data?.data.highlightedPools[index] || null, [data, index])

useEffect(() => {
const handleIndexChange = () => {
setAnimate(true)
setTimeout(() => setIndex(prev => (prev >= 9 ? 0 : prev + 1)), 200)
setTimeout(() => setAnimate(false), 1000)
}
indexInterval = setInterval(handleIndexChange, 4000)

return () => clearInterval(indexInterval)
}, [])

return (
<EarnBannerContainer>
<EarnBannerWrapper onClick={() => navigate({ pathname: APP_PATHS.EARN })}>
<Description>
Explore and Add Liquidity to High-APR Pools <PrimaryText>Instantly</PrimaryText> with{' '}
<PrimaryText>Any Token(s)</PrimaryText> or <PrimaryText>Position</PrimaryText> you choose!
</Description>
<PoolButton
animate={animate}
onClick={e => {
if (!pool) return
e.stopPropagation()
navigate({ pathname: APP_PATHS.EARN, search: `?openPool=${index}` })
}}
>
{!!pool && (
<>
<Flex>
<TokenImage src={pool.tokens[0].logoURI} alt="" />
<TokenImage src={pool.tokens[1].logoURI} alt="" />
<Text fontSize={18} marginLeft={2}>
{pool.tokens[0].symbol}/{pool.tokens[1].symbol}
</Text>
</Flex>
<PoolAprWrapper>
<PoolApr>
{formatAprNumber(pool.apr)}% <AprText>APR</AprText>
</PoolApr>
</PoolAprWrapper>
</>
)}
</PoolButton>
</EarnBannerWrapper>
</EarnBannerContainer>
)
}
11 changes: 7 additions & 4 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,13 @@ export const APP_PATHS = {
REFFERAL_CAMPAIGN: '/campaigns/referrals',
MY_DASHBOARD: '/campaigns/dashboard',

EARN: '/earns',
EARN_POOLS: '/earns/pools',
EARN_POSITIONS: '/earns/positions',
EARN_POSITION_DETAIL: '/earns/position/:chainId/:id',
EARN: '/earn',
EARN_POOLS: '/earn/pools',
EARN_POSITIONS: '/earn/positions',
EARN_POSITION_DETAIL: '/earn/position/:chainId/:id',
EARNS: '/earns',
EARNS_POOLS: '/earns/pools',
EARNS_POSITIONS: '/earns/positions',
} as const

export const TERM_FILES_PATH = {
Expand Down
4 changes: 4 additions & 0 deletions src/pages/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,10 @@ export default function App() {
<Route path={APP_PATHS.EARN_POSITIONS} element={<EarnUserPositions />} />
<Route path={APP_PATHS.EARN_POSITION_DETAIL} element={<EarnPositionDetail />} />

<Route path={APP_PATHS.EARNS} element={<Navigate to={APP_PATHS.EARN} replace />} />
<Route path={APP_PATHS.EARNS_POOLS} element={<Navigate to={APP_PATHS.EARN_POOLS} replace />} />
<Route path={APP_PATHS.EARNS_POSITIONS} element={<Navigate to={APP_PATHS.EARN_POSITIONS} replace />} />

<Route path="*" element={<RedirectPathToSwapV3Network />} />
</Routes>
</BodyWrapper>
Expand Down
13 changes: 9 additions & 4 deletions src/pages/Earns/PoolExplorer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { t } from '@lingui/macro'
import { useEffect, useState } from 'react'
import { Info, Star } from 'react-feather'
import { useSearchParams } from 'react-router-dom'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { useMedia } from 'react-use'
import { Flex, Text } from 'rebass'
import { usePoolsExplorerQuery } from 'services/zapEarn'
Expand All @@ -20,6 +20,7 @@ import useTheme from 'hooks/useTheme'
import SortIcon, { Direction } from 'pages/MarketOverview/SortIcon'
import { MEDIA_WIDTHS } from 'theme'

import { IconArrowLeft } from '../PositionDetail/styles'
import useLiquidityWidget from '../useLiquidityWidget'
import useSupportedDexesAndChains from '../useSupportedDexesAndChains'
import DropdownMenu, { MenuOption } from './DropdownMenu'
Expand Down Expand Up @@ -88,6 +89,7 @@ const Earn = () => {
const [search, setSearch] = useState('')
const deboundedSearch = useDebounce(search, 300)
const [searchParams] = useSearchParams()
const navigate = useNavigate()
const theme = useTheme()
const { filters, updateFilters } = useFilter(setSearch)
const { liquidityWidget, handleOpenZapInWidget } = useLiquidityWidget()
Expand Down Expand Up @@ -139,9 +141,12 @@ const Earn = () => {
{liquidityWidget}

<div>
<Text as="h1" fontSize={24} fontWeight="500">
{t`Earning with Smart Liquidity Providing`}
</Text>
<Flex sx={{ gap: 3 }}>
<IconArrowLeft onClick={() => navigate(-1)} />
<Text as="h1" fontSize={24} fontWeight="500">
{t`Earning with Smart Liquidity Providing`}
</Text>
</Flex>
<Text color={theme.subText} marginTop="8px" fontStyle={'italic'}>
{t`KyberSwap Zap: Instantly add liquidity to high-APY pools using any token(s) or your existing liquidity position with KyberZap`}
</Text>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Earns/PositionDetail/styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const IconArrowLeft = styled(IconArrowLeftSvg)`
cursor: pointer;
position: relative;
top: 5px;
color: ${({ theme }) => theme.subText};
color: rgba(250, 250, 250, 1);
:hover {
filter: brightness(1.5);
Expand Down
11 changes: 7 additions & 4 deletions src/pages/Earns/UserPositions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { shortenAddress } from 'utils'
import { formatDisplayNumber } from 'utils/numbers'

import { CurrencyRoundedImage, CurrencySecondImage, Disclaimer } from '../PoolExplorer/styles'
import { PositionAction as PositionActionBtn } from '../PositionDetail/styles'
import { IconArrowLeft, PositionAction as PositionActionBtn } from '../PositionDetail/styles'
import useLiquidityWidget from '../useLiquidityWidget'
import useSupportedDexesAndChains from '../useSupportedDexesAndChains'
import Filter from './Filter'
Expand Down Expand Up @@ -87,9 +87,12 @@ const MyPositions = () => {
{liquidityWidget}
<PositionPageWrapper>
<div>
<Text as="h1" fontSize={24} fontWeight="500">
{t`My Liquidity`}
</Text>
<Flex sx={{ gap: 3 }}>
<IconArrowLeft onClick={() => navigate(-1)} />
<Text as="h1" fontSize={24} fontWeight="500">
{t`My Liquidity`}
</Text>
</Flex>
<Text color={theme.subText} marginTop="8px" fontStyle={'italic'}>
{t`KyberSwap Zap: Instantly and easily add liquidity to high-APY pools using any token or a combination of tokens.`}
</Text>
Expand Down
22 changes: 21 additions & 1 deletion src/pages/Earns/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ChainId } from '@kyberswap/ks-sdk-core'
import { rgba } from 'polished'
import { useNavigate } from 'react-router-dom'
import { useEffect } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { useMedia } from 'react-use'
import { Box, Flex, Text } from 'rebass'
import { EarnPool, useExplorerLandingQuery } from 'services/zapEarn'
Expand Down Expand Up @@ -285,9 +286,11 @@ const Card = ({

export default function Earns() {
const navigate = useNavigate()
const [searchParams, setSearchParams] = useSearchParams()
const theme = useTheme()
const { account } = useActiveWeb3React()
const { isLoading, data } = useExplorerLandingQuery({ userAddress: account })
const { liquidityWidget, handleOpenZapInWidget } = useLiquidityWidget()

const title = (title: string, tooltip: string, icon: string) => (
<>
Expand Down Expand Up @@ -316,8 +319,25 @@ export default function Earns() {
const upToSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToSmall}px)`)
const upToXXSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToXXSmall}px)`)

useEffect(() => {
const poolsToOpen = data?.data?.highlightedPools || []
const openPool = searchParams.get('openPool')
const openPoolIndex = parseInt(openPool || '', 10)

if (!isNaN(openPoolIndex) && poolsToOpen.length && poolsToOpen[openPoolIndex]) {
searchParams.delete('openPool')
setSearchParams(searchParams)
handleOpenZapInWidget({
exchange: poolsToOpen[openPoolIndex].exchange,
chainId: poolsToOpen[openPoolIndex].chainId,
address: poolsToOpen[openPoolIndex].address,
})
}
}, [handleOpenZapInWidget, data, searchParams, setSearchParams])

return (
<WrapperBg>
{liquidityWidget}
<Container>
<Text fontSize={36} fontWeight="500">
Maximize Your Earnings in DeFi
Expand Down
Loading

0 comments on commit b0b9e75

Please sign in to comment.