Skip to content

Commit

Permalink
feat: rfq dexes grouping (#2569)
Browse files Browse the repository at this point in the history
* feat: rfq dexes grouping

* auto group dex by tag

* fix: search

* chore: env to prod
  • Loading branch information
viet-nv authored Jan 10, 2025
1 parent a7a6d91 commit 3e79cb2
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 136 deletions.
91 changes: 91 additions & 0 deletions src/components/swapv2/LiquiditySourcesPanel/Group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { useEffect, useMemo, useRef } from 'react'

import Checkbox from 'components/CheckBox'
import { useActiveWeb3React } from 'hooks'
import { useAllDexes, useExcludeDexes } from 'state/customizeDexes/hooks'

import { ImageWrapper, Source, SourceName } from './styles'

export const LiquiditySourceGroup = ({
tag,
debouncedSearchText,
}: {
tag: { id: number; name: string }
debouncedSearchText: string
}) => {
const { chainId } = useActiveWeb3React()
const dexes = useAllDexes(chainId)

const dexByTag = useMemo(() => dexes.filter(item => item.tags?.some(t => t.id === tag.id)), [dexes, tag.id])

const [excludeDexes, setExcludeDexes] = useExcludeDexes(chainId)

const handleToggleDex = (id: string) => {
const isExclude = excludeDexes.find(item => item === id)
if (isExclude) {
setExcludeDexes(excludeDexes.filter(item => item !== id))
} else {
setExcludeDexes([...excludeDexes, id])
}
}

const groupRef = useRef<HTMLInputElement>(null)

useEffect(() => {
const selectedDexes = dexByTag?.filter(item => !excludeDexes.includes(item.id)) || []

if (!groupRef.current) return

if (selectedDexes.length === dexByTag?.length) {
groupRef.current.checked = true
groupRef.current.indeterminate = false
} else if (!selectedDexes.length) {
groupRef.current.checked = false
groupRef.current.indeterminate = false
} else if (selectedDexes.length < (dexByTag?.length || 0)) {
groupRef.current.checked = false
groupRef.current.indeterminate = true
}
}, [excludeDexes, dexByTag])

const filteredDexes = dexByTag.filter(item => item.name.toLowerCase().includes(debouncedSearchText))

if (!filteredDexes.length) return null

return (
<>
<Source>
<Checkbox
ref={groupRef}
checked={!dexByTag.map(i => i.id).every(item => excludeDexes.includes(item))}
onChange={e => {
if (e.target.checked) {
setExcludeDexes(excludeDexes.filter(item => !dexByTag.map(d => d.id).includes(item)))
} else {
const newData = [
...excludeDexes.filter(item => dexByTag.map(d => d.id).includes(item)),
...dexByTag.map(item => item.id),
]
setExcludeDexes(newData)
}
}}
/>
<SourceName>{tag.name}</SourceName>
</Source>

{filteredDexes.map(({ name, logoURL, id }) => {
return (
<Source key={name} style={{ padding: '12px 48px' }}>
<Checkbox checked={!excludeDexes.includes(id)} onChange={() => handleToggleDex(id)} />

<ImageWrapper>
<img src={logoURL} alt="" />
</ImageWrapper>

<SourceName>{name}</SourceName>
</Source>
)
})}
</>
)
}
111 changes: 15 additions & 96 deletions src/components/swapv2/LiquiditySourcesPanel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChainId } from '@kyberswap/ks-sdk-core'
import { Trans, t } from '@lingui/macro'
import React, { useEffect, useMemo, useRef, useState } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import { ArrowLeft } from 'react-feather'
import { Box, Flex, Text } from 'rebass'
import styled from 'styled-components'
Expand All @@ -9,7 +9,9 @@ import Checkbox from 'components/CheckBox'
import useDebounce from 'hooks/useDebounce'
import { useAllDexes, useExcludeDexes } from 'state/customizeDexes/hooks'

import { LiquiditySourceGroup } from './Group'
import SearchBar from './SearchBar'
import { ImageWrapper, Source, SourceName } from './styles'

type Props = {
onBack: () => void
Expand Down Expand Up @@ -63,36 +65,6 @@ const SourceList = styled.div`
}
`

const Source = styled.div`
width: 100%;
height: 32px;
display: flex;
align-items: center;
column-gap: 16px;
padding: 12px;
`

const ImageWrapper = styled.div`
width: 32px;
height: 32px;
display: flex;
align-items: center;
img {
width: 100%;
height: auto;
}
`

const SourceName = styled.span`
font-size: 14px;
font-weight: 400;
line-height: 20px;
color: ${({ theme }) => theme.text};
`

const LiquiditySourceHeader = styled.div`
border-top-right-radius: 8px;
border-top-left-radius: 8px;
Expand All @@ -107,17 +79,21 @@ const LiquiditySourceHeader = styled.div`
align-items: center;
`

export const isKyberSwapDex = (id: string) => id.toLowerCase().includes('kyber')

const LiquiditySourcesPanel: React.FC<Props> = ({ onBack, chainId }) => {
const [searchText, setSearchText] = useState('')
const debouncedSearchText = useDebounce(searchText.toLowerCase(), 200).trim()

const dexes = useAllDexes(chainId)
const [excludeDexes, setExcludeDexes] = useExcludeDexes(chainId)

const tagMap: { [key: number]: { name: string; id: number; logoURL: string } } = {}
dexes?.forEach(item => {
item.tags?.forEach(tag => {
tagMap[tag.id] = tag
})
})

const checkAllRef = useRef<HTMLInputElement | null>(null)
const kyberSwapRef = useRef<HTMLInputElement>(null)

useEffect(() => {
const selectedDexes = dexes?.filter(item => !excludeDexes.includes(item.id)) || []
Expand All @@ -136,26 +112,6 @@ const LiquiditySourcesPanel: React.FC<Props> = ({ onBack, chainId }) => {
}
}, [excludeDexes, dexes])

const ksDexes = useMemo(
() => dexes.filter(item => isKyberSwapDex(item.id)).sort((a, b) => a.sortId - b.sortId),
[dexes],
)

useEffect(() => {
if (!kyberSwapRef.current) return
const ksDexesId = ksDexes.map(i => i.id)
if (ksDexesId.every(item => excludeDexes.includes(item))) {
kyberSwapRef.current.checked = false
kyberSwapRef.current.indeterminate = false
} else if (ksDexesId.some(item => excludeDexes.includes(item))) {
kyberSwapRef.current.checked = false
kyberSwapRef.current.indeterminate = true
} else {
kyberSwapRef.current.checked = true
kyberSwapRef.current.indeterminate = false
}
}, [excludeDexes, ksDexes])

const handleToggleDex = (id: string) => {
const isExclude = excludeDexes.find(item => item === id)
if (isExclude) {
Expand Down Expand Up @@ -204,51 +160,14 @@ const LiquiditySourcesPanel: React.FC<Props> = ({ onBack, chainId }) => {
</LiquiditySourceHeader>

<SourceList>
{!!ksDexes.filter(item => item.name.toLowerCase().includes(debouncedSearchText)).length && (
<>
<Source>
<Checkbox
ref={kyberSwapRef}
checked={!ksDexes.map(i => i.id).every(item => excludeDexes.includes(item))}
onChange={e => {
if (e.target.checked) {
setExcludeDexes(excludeDexes.filter(item => !isKyberSwapDex(item)))
} else {
const newData = [
...excludeDexes.filter(item => !isKyberSwapDex(item)),
...ksDexes.map(item => item.id),
]
setExcludeDexes(newData)
}
}}
/>
<ImageWrapper>
<img src="https://kyberswap.com/favicon.ico" alt="ks logo" />
</ImageWrapper>
<SourceName>KyberSwap - All</SourceName>
</Source>

{ksDexes
.filter(item => item.name.toLowerCase().includes(debouncedSearchText))
.map(({ name, logoURL, id }) => {
return (
<Source key={name} style={{ padding: '12px 48px' }}>
<Checkbox checked={!excludeDexes.includes(id)} onChange={() => handleToggleDex(id)} />

<ImageWrapper>
<img src={logoURL} alt="" />
</ImageWrapper>
{Object.values(tagMap).map(tag => {
return <LiquiditySourceGroup tag={tag} debouncedSearchText={debouncedSearchText} key={tag.id} />
})}

<SourceName>{name}</SourceName>
</Source>
)
})}
</>
)}
{dexes
?.filter(item => !isKyberSwapDex(item.id) && item.name.toLowerCase().includes(debouncedSearchText))
?.filter(item => !item.tags && item.name.toLowerCase().includes(debouncedSearchText))
.map(({ name, logoURL, id }) => (
<Source key={name}>
<Source key={id}>
<Checkbox checked={!excludeDexes.includes(id)} onChange={() => handleToggleDex(id)} />

<ImageWrapper>
Expand Down
31 changes: 31 additions & 0 deletions src/components/swapv2/LiquiditySourcesPanel/styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import styled from 'styled-components'

export const Source = styled.div`
width: 100%;
height: 32px;
display: flex;
align-items: center;
column-gap: 16px;
padding: 12px;
`

export const ImageWrapper = styled.div`
width: 32px;
height: 32px;
display: flex;
align-items: center;
img {
width: 100%;
height: auto;
}
`

export const SourceName = styled.span`
font-size: 14px;
font-weight: 400;
line-height: 20px;
color: ${({ theme }) => theme.text};
`
22 changes: 0 additions & 22 deletions src/constants/dexes.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/state/customizeDexes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface Dex {
logoURL: string
id: string
sortId: number
tags?: { name: string; id: number; logoURL: string }[]
}

interface CustomizeDexeState {
Expand Down
20 changes: 2 additions & 18 deletions src/state/customizeDexes/updater.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@ import { useEffect, useMemo } from 'react'
import { useDispatch } from 'react-redux'
import ksSettingApi from 'services/ksSetting'

import { isKyberSwapDex } from 'components/swapv2/LiquiditySourcesPanel'
import { KYBERSWAP_KS_DEXES_TO_UI_DEXES, KYBERSWAP_UI_DEXES_CUSTOM } from 'constants/dexes'
import { NETWORKS_INFO } from 'constants/networks'
import { useActiveWeb3React } from 'hooks'
import { AppDispatch } from 'state/index'
import { uniqueArray } from 'utils/array'

import { Dex, updateAllDexes } from '.'

Expand All @@ -22,21 +19,8 @@ export default function Updater({ customChainId }: { customChainId?: ChainId }):
// filterout kyberswap dexes, will hardcode
const normalizeDexes = useMemo(() => {
const dexesFormatted: Dex[] = dexes?.map(item => ({ ...item, id: item.dexId, sortId: item.id })) || []
const dexesOutsideKyberswap = dexesFormatted.filter(item => !isKyberSwapDex(item.id))
const dexesKyberswap = uniqueArray(
dexesFormatted.filter(dex => KYBERSWAP_KS_DEXES_TO_UI_DEXES[dex.id]),
dex => KYBERSWAP_KS_DEXES_TO_UI_DEXES[dex.id],
)
const dexesUIKyberswap = dexesKyberswap.map(dex => {
const custom = KYBERSWAP_UI_DEXES_CUSTOM[KYBERSWAP_KS_DEXES_TO_UI_DEXES[dex.id] || ''] || dex
return {
...custom,
sortId: dex.sortId,
logoURL: 'https://kyberswap.com/favicon.ico',
}
})

return [...dexesOutsideKyberswap, ...dexesUIKyberswap]

return dexesFormatted
}, [dexes])

useEffect(() => {
Expand Down

0 comments on commit 3e79cb2

Please sign in to comment.