diff --git a/src/components/MultiSelectCombobox.tsx b/src/components/MultiSelectCombobox.tsx index 17badef6d..754a41621 100644 --- a/src/components/MultiSelectCombobox.tsx +++ b/src/components/MultiSelectCombobox.tsx @@ -32,7 +32,9 @@ export const MultiSelectCombobox = ({ const comboboxRef = useRef(null) - const handleSeeMore = () => { + const handleSeeMore = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() const previousCount = viewableMatches setViewableMatches((prev) => prev + 20) diff --git a/src/components/RowLinksWithDropdown/OtherLinks.tsx b/src/components/RowLinksWithDropdown/OtherLinks.tsx index 5f5210538..2bb99cbf9 100644 --- a/src/components/RowLinksWithDropdown/OtherLinks.tsx +++ b/src/components/RowLinksWithDropdown/OtherLinks.tsx @@ -26,7 +26,9 @@ export function OtherLinks({ options, name, isActive, className }: IProps) { const [viewableMatches, setViewableMatches] = useState(20) const comboboxRef = useRef(null) - const handleSeeMore = () => { + const handleSeeMore = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() const previousCount = viewableMatches setViewableMatches((prev) => prev + 20) diff --git a/src/components/Select.tsx b/src/components/Select.tsx index 6805a1770..bc5e551e2 100644 --- a/src/components/Select.tsx +++ b/src/components/Select.tsx @@ -41,7 +41,9 @@ export function Select({ const selectRef = React.useRef(null) - const handleSeeMore = () => { + const handleSeeMore = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() const previousCount = viewableMatches setViewableMatches((prev) => prev + 20) @@ -104,7 +106,11 @@ export function Select({ setValueOnClick={false} hideOnClick={false} className="w-full cursor-pointer px-3 py-4 text-(--link) hover:bg-(--link-hover-bg) focus-visible:bg-(--link-hover-bg) data-active-item:bg-(--link-hover-bg)" - onClick={() => setViewableMatches((prev) => prev + 20)} + onClick={(e) => { + e.preventDefault() + e.stopPropagation() + setViewableMatches((prev) => prev + 20) + }} > See more... diff --git a/src/components/SelectWithCombobox.tsx b/src/components/SelectWithCombobox.tsx index 4981b1514..1623835bc 100644 --- a/src/components/SelectWithCombobox.tsx +++ b/src/components/SelectWithCombobox.tsx @@ -63,7 +63,9 @@ export function SelectWithCombobox({ const comboboxRef = React.useRef(null) - const handleSeeMore = () => { + const handleSeeMore = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() const previousCount = viewableMatches setViewableMatches((prev) => prev + 20) @@ -140,7 +142,11 @@ export function SelectWithCombobox({ setValueOnClick={false} hideOnClick={false} className="w-full cursor-pointer px-3 py-4 text-(--link) hover:bg-(--link-hover-bg) focus-visible:bg-(--link-hover-bg) data-active-item:bg-(--link-hover-bg)" - onClick={() => setViewableMatches((prev) => prev + 20)} + onClick={(e) => { + e.preventDefault() + e.stopPropagation() + setViewableMatches((prev) => prev + 20) + }} > See more... diff --git a/src/containers/Bridges/BridgeChainSelector.tsx b/src/containers/Bridges/BridgeChainSelector.tsx index b5ee59751..af80560c1 100644 --- a/src/containers/Bridges/BridgeChainSelector.tsx +++ b/src/containers/Bridges/BridgeChainSelector.tsx @@ -25,7 +25,9 @@ export function BridgeChainSelector({ options, currentChain, handleClick }: IPro const comboboxRef = React.useRef(null) - const handleSeeMore = () => { + const handleSeeMore = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() const previousCount = viewableMatches setViewableMatches((prev) => prev + 20) diff --git a/src/containers/Raises/Search.tsx b/src/containers/Raises/Search.tsx index ff9af5db9..7a36f4a18 100644 --- a/src/containers/Raises/Search.tsx +++ b/src/containers/Raises/Search.tsx @@ -22,7 +22,9 @@ export function RaisesSearch({ list }) { const comboboxRef = useRef(null) - const handleSeeMore = () => { + const handleSeeMore = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() const previousCount = viewableMatches setViewableMatches((prev) => prev + 20) diff --git a/src/containers/Yields/Filters/IncludeExcludeTokens.tsx b/src/containers/Yields/Filters/IncludeExcludeTokens.tsx index f4979c12d..571a984d0 100644 --- a/src/containers/Yields/Filters/IncludeExcludeTokens.tsx +++ b/src/containers/Yields/Filters/IncludeExcludeTokens.tsx @@ -89,7 +89,9 @@ export function IncludeExcludeTokens({ const tokensComboboxRef = useRef(null) const pairsComboboxRef = useRef(null) - const handleTokensSeeMore = () => { + const handleTokensSeeMore = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() const previousCount = tokensViewableMatches setTokensViewableMatches((prev) => prev + 20) @@ -103,7 +105,9 @@ export function IncludeExcludeTokens({ }, 0) } - const handlePairsSeeMore = () => { + const handlePairsSeeMore = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() const previousCount = pairsViewableMatches setPairsViewableMatches((prev) => prev + 20) diff --git a/src/containers/Yields/Search.tsx b/src/containers/Yields/Search.tsx index c0b6fb824..58005724d 100644 --- a/src/containers/Yields/Search.tsx +++ b/src/containers/Yields/Search.tsx @@ -31,7 +31,9 @@ export function YieldsSearch({ const comboboxRef = React.useRef(null) - const handleSeeMore = () => { + const handleSeeMore = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() const previousCount = viewableMatches setViewableMatches((prev) => prev + 20) diff --git a/src/pages/borrow.tsx b/src/pages/borrow.tsx index 1c1bd2aa6..33bd950fe 100644 --- a/src/pages/borrow.tsx +++ b/src/pages/borrow.tsx @@ -192,7 +192,9 @@ const TokensSelect = ({ const comboboxRef = React.useRef(null) - const handleSeeMore = () => { + const handleSeeMore = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() const previousCount = viewableMatches setViewableMatches((prev) => prev + 20) diff --git a/src/pages/compare-protocols.tsx b/src/pages/compare-protocols.tsx index c16ed8e36..a59619bfb 100644 --- a/src/pages/compare-protocols.tsx +++ b/src/pages/compare-protocols.tsx @@ -2,19 +2,18 @@ import * as React from 'react' import { useRouter } from 'next/router' import { useQueries } from '@tanstack/react-query' import { maxAgeForNext } from '~/api' -import { getSimpleProtocolsPageData } from '~/api/categories/protocols' -import { basicPropertiesToKeep } from '~/api/categories/protocols/utils' import { ILineAndBarChartProps } from '~/components/ECharts/types' -import { IconsRow } from '~/components/IconsRow' +import { tvlOptions } from '~/components/Filters/options' import { LocalLoader } from '~/components/Loaders' import { MultiSelectCombobox } from '~/components/MultiSelectCombobox' -import { TokenLogo } from '~/components/TokenLogo' import { PROTOCOL_API } from '~/constants' -import { CHART_COLORS } from '~/constants/colors' -import { Flag } from '~/containers/ProtocolOverview/Flag' +import { getChainOverviewData } from '~/containers/ChainOverview/queries.server' +import { ChainProtocolsTable } from '~/containers/ChainOverview/Table' +import { IChainOverviewData } from '~/containers/ChainOverview/types' import { useLocalStorageSettingsManager } from '~/contexts/LocalStorage' +import { formatProtocolsList2 } from '~/hooks/data/defi' import Layout from '~/layout' -import { formattedNum, getNDistinctColors, getPercentChange, slug, tokenIconUrl } from '~/utils' +import { getNDistinctColors, slug, tokenIconUrl } from '~/utils' import { fetchJson } from '~/utils/async' import { withPerformanceLogging } from '~/utils/perf' @@ -23,11 +22,32 @@ const LineAndBarChart = React.lazy( ) as React.FC export const getStaticProps = withPerformanceLogging('comparison', async () => { - const { protocols } = await getSimpleProtocolsPageData([...basicPropertiesToKeep, 'logo']) + const { protocols } = await getChainOverviewData({ chain: 'All' }) return { props: { - protocols + protocols, + protocolsList: protocols + .reduce((acc, protocol) => { + acc.push({ + value: protocol.name, + label: protocol.name, + logo: tokenIconUrl(protocol.name), + score: protocol.tvl?.default?.tvl ?? 0 + }) + if (protocol.childProtocols) { + for (const childProtocol of protocol.childProtocols) { + acc.push({ + value: childProtocol.name, + label: childProtocol.name, + logo: tokenIconUrl(childProtocol.name), + score: 1 + }) + } + } + return acc + }, []) + .sort((a, b) => b.score - a.score) }, revalidate: maxAgeForNext([22]) } @@ -50,13 +70,15 @@ const fetchProtocol = async (selectedProtocol: string | null) => { const pageName = ['Compare Protocols'] -export default function CompareProtocolsTvls({ protocols }) { +export default function CompareProtocols({ + protocols, + protocolsList +}: { + protocols: IChainOverviewData['protocols'] + protocolsList: Array<{ value: string; label: string; logo: string }> +}) { const router = useRouter() - const protocolsNames = React.useMemo(() => { - return protocols.map((p) => p.name) - }, [protocols]) - const [extraTvlEnabled] = useLocalStorageSettingsManager('tvl') const { protocol } = router.query @@ -77,7 +99,16 @@ export default function CompareProtocolsTvls({ protocols }) { const isLoading = results.some((r) => r.isLoading) - // TODO handle extra tvl settings + const minTvl = + typeof router.query.minTvl === 'string' && router.query.minTvl !== '' && !Number.isNaN(Number(router.query.minTvl)) + ? +router.query.minTvl + : null + + const maxTvl = + typeof router.query.maxTvl === 'string' && router.query.maxTvl !== '' && !Number.isNaN(Number(router.query.maxTvl)) + ? +router.query.maxTvl + : null + const { charts } = React.useMemo(() => { const formattedData = results @@ -87,11 +118,7 @@ export default function CompareProtocolsTvls({ protocols }) { protocolName: res.data.protocolData.name })) ?? [] - // Generate distinct colors for all protocols - const protocolNames = formattedData.map((p) => p.protocolName) - const distinctColors = getNDistinctColors(protocolNames.length) - const stackColors = Object.fromEntries(protocolNames.map((name, index) => [name, distinctColors[index]])) - + let totalProtocols = 0 const chartsByProtocol = {} for (const protocol of formattedData) { @@ -113,12 +140,15 @@ export default function CompareProtocolsTvls({ protocols }) { (chartsByProtocol[protocol.protocolName][dateKey] || 0) + totalLiquidityUSD } } + totalProtocols++ } + const distinctColors = getNDistinctColors(totalProtocols) + const charts = {} let chartIndex = 0 for (const protocol in chartsByProtocol) { - const color = stackColors[protocol] + const color = distinctColors[chartIndex] charts[protocol] = { name: protocol, @@ -139,25 +169,29 @@ export default function CompareProtocolsTvls({ protocols }) { } }, [results, extraTvlEnabled]) - const sortedProtocols = React.useMemo(() => { - const selectedSet = new Set(selectedProtocols) - const unselectedProtocols = protocolsNames.filter((protocol) => !selectedSet.has(protocol)) - - return [...selectedProtocols, ...unselectedProtocols].map((protocol) => ({ - value: protocol, - label: protocol, - logo: tokenIconUrl(protocol) - })) - }, [selectedProtocols, protocolsNames]) - - const selectedProtocolsData = React.useMemo(() => { - return selectedProtocols - .map((protocolName) => { - return protocols.find((p) => p.name === protocolName) - }) - .filter(Boolean) + const { selectedProtocolsData, selectedProtocolsNames } = React.useMemo(() => { + const selectedProtocolsData = selectedProtocols.reduce((acc, protocolName) => { + const data = protocols.find( + (p) => p.name === protocolName || p.childProtocols?.find((cp) => cp.name === protocolName) + ) + if (!data) return acc + if (data.name === protocolName) { + acc.push(data) + } else { + const child = data.childProtocols?.find((cp) => cp.name === protocolName) + if (child) { + acc.push(child) + } + } + return acc + }, []) + return { selectedProtocolsData, selectedProtocolsNames: selectedProtocolsData.map((p) => p.name) } }, [selectedProtocols, protocols]) + const protocolsTableData = React.useMemo(() => { + return formatProtocolsList2({ protocols: selectedProtocolsData, extraTvlsEnabled: extraTvlEnabled, minTvl, maxTvl }) + }, [selectedProtocolsData, extraTvlEnabled, minTvl, maxTvl]) + return (
{ router.push( { @@ -187,9 +222,8 @@ export default function CompareProtocolsTvls({ protocols }) { }} />
- {selectedProtocols.length > 1 ? ( -
+
{isLoading || !router.isReady ? (
@@ -201,11 +235,12 @@ export default function CompareProtocolsTvls({ protocols }) { )}
-
- {selectedProtocolsData.map((protocolData) => ( - - ))} -
+ + {protocolsTableData.length && ( +
+ +
+ )}
) : (
@@ -215,124 +250,3 @@ export default function CompareProtocolsTvls({ protocols }) { ) } - -const ProtocolInfoCard = ({ protocolData }) => { - const [extraTvlsEnabled] = useLocalStorageSettingsManager('tvl') - - let tvl = protocolData.tvl || 0 - let tvlPrevDay = protocolData.tvlPrevDay || 0 - let tvlPrevWeek = protocolData.tvlPrevWeek || 0 - let tvlPrevMonth = protocolData.tvlPrevMonth || 0 - - for (const tvlKey in protocolData.extraTvl) { - if (extraTvlsEnabled[tvlKey] && tvlKey !== 'doublecounted' && tvlKey !== 'liquidstaking') { - tvl = tvl + (protocolData.extraTvl[tvlKey].tvl || 0) - tvlPrevDay = tvlPrevDay + (protocolData.extraTvl[tvlKey].tvlPrevDay || 0) - tvlPrevWeek = tvlPrevWeek + (protocolData.extraTvl[tvlKey].tvlPrevWeek || 0) - tvlPrevMonth = tvlPrevMonth + (protocolData.extraTvl[tvlKey].tvlPrevMonth || 0) - } - } - - let change1d = getPercentChange(tvl, tvlPrevDay) - let change7d = getPercentChange(tvl, tvlPrevWeek) - let change1m = getPercentChange(tvl, tvlPrevMonth) - - return ( -
-
- - {protocolData.name || ''} - {protocolData.symbol && protocolData.symbol !== '-' ? ( - ({protocolData.symbol}) - ) : null} -
- - - - - - {protocolData.chains.length > 0 && ( -
-

Chains

- -
- )} -
- ) -} - -const CompactProtocolTVL = ({ tvl, name, category }) => { - return ( -
- - Total Value Locked - - - - {formattedNum(tvl, true)} - -
- ) -} - -const KeyMetrics = ({ change_1d, change_7d, change_1m, protocolName }) => { - const metrics = [] - - if (change_1d != null) { - metrics.push({ - name: 'Change 24h', - value: `${change_1d > 0 ? '+' : ''}${change_1d.toFixed(2)}%`, - isPercentage: true - }) - } - - if (change_7d != null) { - metrics.push({ - name: 'Change 7d', - value: `${change_7d > 0 ? '+' : ''}${change_7d.toFixed(2)}%`, - isPercentage: true - }) - } - - if (change_1m != null) { - metrics.push({ - name: 'Change 30d', - value: `${change_1m > 0 ? '+' : ''}${change_1m.toFixed(2)}%`, - isPercentage: true - }) - } - - if (metrics.length === 0) { - return null - } - - return ( -
-
- {metrics.map((metric) => ( -
- {metric.name} - 0 - ? 'text-(--success)' - : parseFloat(metric.value) < 0 - ? 'text-(--error)' - : '' - : '' - }`} - > - {metric.isPercentage ? metric.value : formattedNum(metric.value, true)} - -
- ))} -
-
- ) -} diff --git a/src/pages/directory.tsx b/src/pages/directory.tsx index 3a29c2355..0a0e4c91d 100644 --- a/src/pages/directory.tsx +++ b/src/pages/directory.tsx @@ -41,7 +41,9 @@ export default function Protocols({ protocols }: { protocols: Array<{ name: stri const comboboxRef = useRef(null) - const handleSeeMore = () => { + const handleSeeMore = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() const previousCount = viewableMatches setViewableMatches((prev) => prev + 20) diff --git a/src/pages/token-usage.tsx b/src/pages/token-usage.tsx index d2ae28da7..1015b13f5 100644 --- a/src/pages/token-usage.tsx +++ b/src/pages/token-usage.tsx @@ -233,7 +233,9 @@ const Search = ({ searchData }: { searchData: ISearchData[] }) => { const comboboxRef = useRef(null) - const handleSeeMore = () => { + const handleSeeMore = (e: React.MouseEvent) => { + e.preventDefault() + e.stopPropagation() const previousCount = viewableMatches setViewableMatches((prev) => prev + 20)