diff --git a/lib/modules/marketing/useEcosystemPoolActivity.tsx b/lib/modules/marketing/useEcosystemPoolActivity.tsx
index 456ff83b4..aacd1e11d 100644
--- a/lib/modules/marketing/useEcosystemPoolActivity.tsx
+++ b/lib/modules/marketing/useEcosystemPoolActivity.tsx
@@ -2,15 +2,23 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import * as echarts from 'echarts/core'
-import { useEffect, useMemo, useRef, useState } from 'react'
+import { useEffect, useMemo, useRef, useState, FC, memo, useCallback, ReactNode } from 'react'
import { format } from 'date-fns'
import { GqlChain, GqlPoolEventType, GqlToken } from '@/lib/shared/services/api/generated/graphql'
import EChartsReactCore from 'echarts-for-react/lib/core'
-import { ColorMode, useTheme as useChakraTheme } from '@chakra-ui/react'
-import { useTheme as useNextTheme } from 'next-themes'
+import {
+ Box,
+ Card,
+ ChakraProvider,
+ useTheme,
+ Text,
+ Link,
+ Image,
+ VStack,
+ HStack,
+} from '@chakra-ui/react'
import { abbreviateAddress } from '@/lib/shared/utils/addresses'
import { useTokens } from '@/lib/modules/tokens/TokensProvider'
-
import {
getBlockExplorerAddressUrl,
getBlockExplorerTxUrl,
@@ -21,6 +29,7 @@ import { NumberFormatter } from '@/lib/shared/utils/numbers'
import { usePoolEvents } from '../pool/usePoolEvents'
import { supportedNetworks } from '../web3/ChainConfig'
import { getChainShortName } from '@/lib/config/app.config'
+import { ArrowUpRight } from 'react-feather'
type ChartInfoTokens = {
token?: GqlToken
@@ -108,27 +117,88 @@ function getDefaultChainMeta() {
}
}
+const CustomTooltip: FC<{
+ params: any
+ currencyFormatter: (value: string) => string
+ theme: any
+}> = ({ params, currencyFormatter, theme }) => {
+ const data = Array.isArray(params) ? params[0] : params
+ const timestamp = data.value[0]
+ const metaData = data.data[2] as ChartInfoMetaData
+ const { userAddress, tokens, usdValue, tx, chain, type } = metaData
+ const txLink = getBlockExplorerTxUrl(tx, chain)
+ const addressLink = getBlockExplorerAddressUrl(userAddress, chain)
+
+ const typeStr =
+ type === GqlPoolEventType.Add ? 'Add' : type === GqlPoolEventType.Remove ? 'Remove' : 'Swap'
+
+ return (
+
+
+
+
+ {`${typeStr} ${currencyFormatter(
+ usdValue
+ )}`}
+
+ on {getChainShortName(chain)}
+
+
+
+ {tokens
+ .filter(token => token.token && Number(token.amount) !== 0)
+ .map((token, index) => (
+
+
+
+ {Number(Number(token.amount).toFixed(2)).toLocaleString()} {token.token?.symbol}
+
+
+ ))}
+
+
+
+
+
+ Tx: {format(new Date(timestamp * 1000), 'MMM d, h:mma').toLowerCase()}
+
+
+
+
+
+
+
+
+
+ By: {abbreviateAddress(userAddress)}
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const MemoizedCustomTooltip = memo(CustomTooltip)
+
const getDefaultPoolActivityChartOptions = (
- nextTheme: ColorMode = 'dark',
theme: any, // TODO: type this
currencyFormatter: NumberFormatter,
isMobile = false,
- is2xl = false
+ is2xl = false,
+ tooltipFormatter: (params: any) => string
// chain: GqlChain
): echarts.EChartsCoreOption => {
- const toolTipTheme = {
- heading: 'font-weight: bold; color: #E5D3BE',
- container: `background: ${
- nextTheme === 'dark'
- ? theme.semanticTokens.colors.background.level3._dark
- : theme.semanticTokens.colors.background.default
- };`,
- text:
- nextTheme === 'dark'
- ? theme.semanticTokens.colors.font.primary._dark
- : theme.semanticTokens.colors.font.primary.default,
- }
-
return {
grid: {
left: isMobile ? '15%' : '5.5%',
@@ -175,92 +245,13 @@ const getDefaultPoolActivityChartOptions = (
splitNumber: 3,
},
tooltip: {
- triggerOn: 'mousemove|click',
+ trigger: 'item',
confine: is2xl ? false : true,
enterable: true,
hideDelay: 300,
- position: function (point: number[]) {
- return [point[0] + 5, point[1] - 5]
- },
- extraCssText: `padding-right:2rem;border: none;${toolTipTheme.container};pointer-events: auto!important`,
- formatter: (params: any) => {
- const data = Array.isArray(params) ? params[0] : params
- const timestamp = data.value[0]
- const metaData = data.data[2] as ChartInfoMetaData
- const userAddress = metaData.userAddress
- const tokens = metaData.tokens.filter(token => {
- if (!token.token) return false
- if (Number(token.amount) === 0) return false
- return true
- }) as ChartInfoTokens[]
-
- const tx = metaData.tx
- const txLink = getBlockExplorerTxUrl(tx, metaData.chain)
- const addressLink = getBlockExplorerAddressUrl(userAddress, metaData.chain)
- const typeStr =
- metaData.type === GqlPoolEventType.Add
- ? 'Add'
- : metaData.type === GqlPoolEventType.Remove
- ? 'Remove'
- : 'Swap'
-
- const arrow = ``
-
- return `
-
- `
- },
+ position: [0, 0], // This will be overridden by our custom positioning
+ extraCssText: `padding-right: 2rem;border: none;background: transparent;pointer-events: auto!important;box-shadow: none;`,
+ formatter: tooltipFormatter,
},
}
}
@@ -309,19 +300,50 @@ const tabsList: PoolActivityChartTypeTab[] = [
export function useEcosystemPoolActivityChart() {
const eChartsRef = useRef(null)
const { isMobile, is2xl } = useBreakpoints()
- const { theme: nextTheme } = useNextTheme()
const { getToken } = useTokens()
const { toCurrency } = useCurrency()
const [activeTab, setActiveTab] = useState(tabsList[0])
const [activeNetwork, setActiveNetwork] = useState('all')
+ const [tooltipContent, setTooltipContent] = useState(null)
+ const [tooltipPosition, setTooltipPosition] = useState({ x: 0, y: 0 })
- const theme = useChakraTheme()
+ const theme = useTheme()
const { loading, data: response } = usePoolEvents({
first: 500,
chainIn: supportedNetworks,
})
+ const tooltipFormatter = useCallback(
+ (params: any) => {
+ setTooltipContent(
+
+
+
+ )
+ return ' '
+ },
+ [theme, toCurrency]
+ )
+
+ const onEvents = useMemo(
+ () => ({
+ mousemove: (params: any) => {
+ if (params.componentType === 'series') {
+ setTooltipPosition({ x: params.event.offsetX, y: params.event.offsetY })
+ }
+ },
+ mouseout: () => {
+ setTooltipContent(null)
+ },
+ }),
+ []
+ )
+
+ const memoizedChartOptions = useMemo(() => {
+ return getDefaultPoolActivityChartOptions(theme, toCurrency, isMobile, is2xl, tooltipFormatter)
+ }, [theme, toCurrency, isMobile, is2xl, tooltipFormatter])
+
const chartData = useMemo(() => {
if (!response) return getDefaultChainMeta()
const { poolEvents: events } = response
@@ -429,13 +451,7 @@ export function useEcosystemPoolActivityChart() {
return {
isLoading: loading,
- chartOption: getDefaultPoolActivityChartOptions(
- nextTheme as ColorMode,
- theme,
- toCurrency,
- isMobile,
- is2xl
- ),
+ chartOption: memoizedChartOptions,
eChartsRef,
chartData,
tabsList,
@@ -444,5 +460,8 @@ export function useEcosystemPoolActivityChart() {
activeNetwork,
setActiveNetwork,
headerInfo,
+ tooltipContent,
+ tooltipPosition,
+ onEvents,
}
}
diff --git a/lib/shared/components/marketing/EcosystemActivityChart.tsx b/lib/shared/components/marketing/EcosystemActivityChart.tsx
index b1b986f18..305145f7c 100644
--- a/lib/shared/components/marketing/EcosystemActivityChart.tsx
+++ b/lib/shared/components/marketing/EcosystemActivityChart.tsx
@@ -14,9 +14,8 @@ import {
VStack,
} from '@chakra-ui/react'
import ButtonGroup from '@/lib/shared/components/btns/button-group/ButtonGroup'
-import { FC, PropsWithChildren } from 'react'
+import { FC, PropsWithChildren, useState, useRef, useCallback, useEffect } from 'react'
import { motion } from 'framer-motion'
-
import { EcosystemChainSelect } from './EcosystemChainSelect'
import { getChainShortName } from '@/lib/config/app.config'
import { supportedNetworks } from '@/lib/modules/web3/ChainConfig'
@@ -25,6 +24,8 @@ import {
gradientMap,
useEcosystemPoolActivityChart,
} from '@/lib/modules/marketing/useEcosystemPoolActivity'
+import { createPortal } from 'react-dom'
+import useMeasure from 'react-use-measure'
const AnimateOpacity: FC> = ({ children }) => (
@@ -45,8 +46,15 @@ export function EcosystemActivityChart() {
tabsList,
headerInfo,
eChartsRef,
+ tooltipContent,
+ tooltipPosition,
+ onEvents,
} = useEcosystemPoolActivityChart()
+ const [ref, bounds] = useMeasure({
+ scroll: true,
+ })
+
const legendTabs = supportedNetworks.map(key => {
return {
label: getChainShortName(key),
@@ -54,11 +62,43 @@ export function EcosystemActivityChart() {
}
})
+ const [isTooltipVisible, setIsTooltipVisible] = useState(false)
+ const tooltipRef = useRef(null)
+ const timeoutRef = useRef(null)
+
+ const showTooltip = useCallback(
+ (params: any) => {
+ onEvents.mousemove(params)
+ setIsTooltipVisible(true)
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current)
+ }
+ },
+ [onEvents]
+ )
+
+ const hideTooltip = useCallback(() => {
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current)
+ }
+ timeoutRef.current = setTimeout(() => {
+ onEvents.mouseout()
+ setIsTooltipVisible(false)
+ }, 100)
+ }, [onEvents])
+
+ useEffect(() => {
+ return () => {
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current)
+ }
+ }
+ }, [])
+
return (
{isLoading && }
-
-
-
{
@@ -98,16 +136,41 @@ export function EcosystemActivityChart() {
-
+
+
+ {/* No idea how to get this to work with */}
+ {createPortal(
+
+ {tooltipContent}
+
,
+ document.body
+ )}
+
-
-
{legendTabs.map((tab, index) => (