From 3e79cb278945fcf38f777f84746f491a397590e2 Mon Sep 17 00:00:00 2001 From: Nguyen Van Viet Date: Fri, 10 Jan 2025 15:27:59 +0700 Subject: [PATCH] feat: rfq dexes grouping (#2569) * feat: rfq dexes grouping * auto group dex by tag * fix: search * chore: env to prod --- .../swapv2/LiquiditySourcesPanel/Group.tsx | 91 ++++++++++++++ .../swapv2/LiquiditySourcesPanel/index.tsx | 111 +++--------------- .../swapv2/LiquiditySourcesPanel/styles.tsx | 31 +++++ src/constants/dexes.ts | 22 ---- src/state/customizeDexes/index.ts | 1 + src/state/customizeDexes/updater.tsx | 20 +--- 6 files changed, 140 insertions(+), 136 deletions(-) create mode 100644 src/components/swapv2/LiquiditySourcesPanel/Group.tsx create mode 100644 src/components/swapv2/LiquiditySourcesPanel/styles.tsx delete mode 100644 src/constants/dexes.ts diff --git a/src/components/swapv2/LiquiditySourcesPanel/Group.tsx b/src/components/swapv2/LiquiditySourcesPanel/Group.tsx new file mode 100644 index 0000000000..6f83ed3d58 --- /dev/null +++ b/src/components/swapv2/LiquiditySourcesPanel/Group.tsx @@ -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(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 ( + <> + + 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) + } + }} + /> + {tag.name} + + + {filteredDexes.map(({ name, logoURL, id }) => { + return ( + + handleToggleDex(id)} /> + + + + + + {name} + + ) + })} + + ) +} diff --git a/src/components/swapv2/LiquiditySourcesPanel/index.tsx b/src/components/swapv2/LiquiditySourcesPanel/index.tsx index 1384558fc4..e4052244b7 100644 --- a/src/components/swapv2/LiquiditySourcesPanel/index.tsx +++ b/src/components/swapv2/LiquiditySourcesPanel/index.tsx @@ -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' @@ -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 @@ -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; @@ -107,8 +79,6 @@ const LiquiditySourceHeader = styled.div` align-items: center; ` -export const isKyberSwapDex = (id: string) => id.toLowerCase().includes('kyber') - const LiquiditySourcesPanel: React.FC = ({ onBack, chainId }) => { const [searchText, setSearchText] = useState('') const debouncedSearchText = useDebounce(searchText.toLowerCase(), 200).trim() @@ -116,8 +86,14 @@ const LiquiditySourcesPanel: React.FC = ({ onBack, chainId }) => { 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(null) - const kyberSwapRef = useRef(null) useEffect(() => { const selectedDexes = dexes?.filter(item => !excludeDexes.includes(item.id)) || [] @@ -136,26 +112,6 @@ const LiquiditySourcesPanel: React.FC = ({ 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) { @@ -204,51 +160,14 @@ const LiquiditySourcesPanel: React.FC = ({ onBack, chainId }) => { - {!!ksDexes.filter(item => item.name.toLowerCase().includes(debouncedSearchText)).length && ( - <> - - 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) - } - }} - /> - - ks logo - - KyberSwap - All - - - {ksDexes - .filter(item => item.name.toLowerCase().includes(debouncedSearchText)) - .map(({ name, logoURL, id }) => { - return ( - - handleToggleDex(id)} /> - - - - + {Object.values(tagMap).map(tag => { + return + })} - {name} - - ) - })} - - )} {dexes - ?.filter(item => !isKyberSwapDex(item.id) && item.name.toLowerCase().includes(debouncedSearchText)) + ?.filter(item => !item.tags && item.name.toLowerCase().includes(debouncedSearchText)) .map(({ name, logoURL, id }) => ( - + handleToggleDex(id)} /> diff --git a/src/components/swapv2/LiquiditySourcesPanel/styles.tsx b/src/components/swapv2/LiquiditySourcesPanel/styles.tsx new file mode 100644 index 0000000000..89b3a8902b --- /dev/null +++ b/src/components/swapv2/LiquiditySourcesPanel/styles.tsx @@ -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}; +` diff --git a/src/constants/dexes.ts b/src/constants/dexes.ts deleted file mode 100644 index 8feef13cf9..0000000000 --- a/src/constants/dexes.ts +++ /dev/null @@ -1,22 +0,0 @@ -// To combine all kyber options 1 option on UI -export const KYBERSWAP_KS_DEXES_TO_UI_DEXES: { [key: string]: string | undefined } = { - kyberswap: 'kyberswapv1', // kyberswap classic old contract - 'kyberswap-static': 'kyberswapv1', // kyberswap classic new contract -> with static fee - 'kyberswap-elastic': 'kyberswap-elastic', - 'kyberswap-limit-order': 'kyberswap-limit-order', - 'kyberswap-limit-order-v2': 'kyberswap-limit-order-v2', - 'kyber-pmm': 'kyber-pmm', -} - -// only put dex need to be custom, otherwise get from admin -export const KYBERSWAP_UI_DEXES_CUSTOM: { - [key: string]: { - name: string - id: string - } -} = { - kyberswapv1: { - name: 'KyberSwap Classic', - id: 'kyberswapv1', - }, -} diff --git a/src/state/customizeDexes/index.ts b/src/state/customizeDexes/index.ts index 2820c21308..6ac996bba5 100644 --- a/src/state/customizeDexes/index.ts +++ b/src/state/customizeDexes/index.ts @@ -6,6 +6,7 @@ export interface Dex { logoURL: string id: string sortId: number + tags?: { name: string; id: number; logoURL: string }[] } interface CustomizeDexeState { diff --git a/src/state/customizeDexes/updater.tsx b/src/state/customizeDexes/updater.tsx index 174f17ab7f..c61814e055 100644 --- a/src/state/customizeDexes/updater.tsx +++ b/src/state/customizeDexes/updater.tsx @@ -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 '.' @@ -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(() => {