fix: change top tokens query return type (#4453)

* query return

* fix return

* rm default empty string

* rm ?

* undef

* undefined

* null

* fix

* handle search

* .

* rebase

* top tokens

* rm blank

* rm console log

* make 100
This commit is contained in:
Kaylee George 2022-08-24 12:38:22 -07:00 committed by GitHub
parent 0e530cf92e
commit ac27c89a44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 90 additions and 132 deletions

@ -7,8 +7,8 @@ import { Line } from '@visx/shape'
import { filterTimeAtom } from 'components/Tokens/state' import { filterTimeAtom } from 'components/Tokens/state'
import { bisect, curveBasis, NumberValue, scaleLinear } from 'd3' import { bisect, curveBasis, NumberValue, scaleLinear } from 'd3'
import { useTokenPriceQuery } from 'graphql/data/TokenPriceQuery' import { useTokenPriceQuery } from 'graphql/data/TokenPriceQuery'
import { TimePeriod } from 'graphql/data/TopTokenQuery'
import { useActiveLocale } from 'hooks/useActiveLocale' import { useActiveLocale } from 'hooks/useActiveLocale'
import { TimePeriod } from 'hooks/useExplorePageQuery'
import { useAtom } from 'jotai' import { useAtom } from 'jotai'
import { useCallback, useState } from 'react' import { useCallback, useState } from 'react'
import { ArrowDownRight, ArrowUpRight } from 'react-feather' import { ArrowDownRight, ArrowUpRight } from 'react-feather'

@ -1,4 +1,4 @@
import { TimePeriod } from 'hooks/useExplorePageQuery' import { TimePeriod } from 'graphql/data/TopTokenQuery'
import { useOnClickOutside } from 'hooks/useOnClickOutside' import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { useAtom } from 'jotai' import { useAtom } from 'jotai'
import { useRef } from 'react' import { useRef } from 'react'

@ -5,8 +5,8 @@ import { EventName } from 'components/AmplitudeAnalytics/constants'
import SparklineChart from 'components/Charts/SparklineChart' import SparklineChart from 'components/Charts/SparklineChart'
import CurrencyLogo from 'components/CurrencyLogo' import CurrencyLogo from 'components/CurrencyLogo'
import { getChainInfo } from 'constants/chainInfo' import { getChainInfo } from 'constants/chainInfo'
import { useCurrency, useToken } from 'hooks/Tokens' import { TimePeriod, TokenData } from 'graphql/data/TopTokenQuery'
import { TimePeriod, TokenData } from 'hooks/useExplorePageQuery' import { useCurrency } from 'hooks/Tokens'
import { useAtom } from 'jotai' import { useAtom } from 'jotai'
import { useAtomValue } from 'jotai/utils' import { useAtomValue } from 'jotai/utils'
import { ReactNode } from 'react' import { ReactNode } from 'react'
@ -245,6 +245,7 @@ const TokenName = styled.div`
` `
const TokenSymbol = styled(Cell)` const TokenSymbol = styled(Cell)`
color: ${({ theme }) => theme.textTertiary}; color: ${({ theme }) => theme.textTertiary};
text-transform: uppercase;
@media only screen and (max-width: ${SMALL_MEDIA_BREAKPOINT}) { @media only screen and (max-width: ${SMALL_MEDIA_BREAKPOINT}) {
font-size: 12px; font-size: 12px;
@ -443,10 +444,9 @@ export default function LoadedRow({
tokenData: TokenData tokenData: TokenData
timePeriod: TimePeriod timePeriod: TimePeriod
}) { }) {
const token = useToken(tokenAddress)
const currency = useCurrency(tokenAddress) const currency = useCurrency(tokenAddress)
const tokenName = token?.name ?? '' const tokenName = tokenData.name
const tokenSymbol = token?.symbol ?? '' const tokenSymbol = tokenData.symbol
const theme = useTheme() const theme = useTheme()
const [favoriteTokens] = useAtom(favoritesAtom) const [favoriteTokens] = useAtom(favoritesAtom)
const isFavorited = favoriteTokens.includes(tokenAddress) const isFavorited = favoriteTokens.includes(tokenAddress)
@ -454,14 +454,14 @@ export default function LoadedRow({
const filterString = useAtomValue(filterStringAtom) const filterString = useAtomValue(filterStringAtom)
const filterNetwork = useAtomValue(filterNetworkAtom) const filterNetwork = useAtomValue(filterNetworkAtom)
const L2Icon = getChainInfo(filterNetwork).circleLogoUrl const L2Icon = getChainInfo(filterNetwork).circleLogoUrl
const delta = tokenData.percentChange[timePeriod]?.value const delta = tokenData.percentChange?.[timePeriod]?.value
const arrow = delta ? getDeltaArrow(delta) : null const arrow = delta ? getDeltaArrow(delta) : null
const formattedDelta = delta ? formatDelta(delta) : null const formattedDelta = delta ? formatDelta(delta) : null
const exploreTokenSelectedEventProperties = { const exploreTokenSelectedEventProperties = {
chain_id: filterNetwork, chain_id: filterNetwork,
token_address: tokenAddress, token_address: tokenAddress,
token_symbol: token?.symbol, token_symbol: tokenSymbol,
token_list_index: tokenListIndex, token_list_index: tokenListIndex,
token_list_length: tokenListLength, token_list_length: tokenListLength,
time_frame: timePeriod, time_frame: timePeriod,

@ -6,8 +6,7 @@ import {
sortCategoryAtom, sortCategoryAtom,
sortDirectionAtom, sortDirectionAtom,
} from 'components/Tokens/state' } from 'components/Tokens/state'
import { useAllTokens } from 'hooks/Tokens' import { TimePeriod, TokenData } from 'graphql/data/TopTokenQuery'
import { TimePeriod, TokenData } from 'hooks/useExplorePageQuery'
import { useAtomValue } from 'jotai/utils' import { useAtomValue } from 'jotai/utils'
import { ReactNode, Suspense, useCallback, useMemo } from 'react' import { ReactNode, Suspense, useCallback, useMemo } from 'react'
import { AlertTriangle } from 'react-feather' import { AlertTriangle } from 'react-feather'
@ -47,34 +46,33 @@ const TokenRowsContainer = styled.div`
width: 100%; width: 100%;
` `
function useFilteredTokens(addresses: string[]) { function useFilteredTokens(tokens: TokenData[] | undefined) {
const filterString = useAtomValue(filterStringAtom) const filterString = useAtomValue(filterStringAtom)
const favoriteTokens = useAtomValue(favoritesAtom) const favoriteTokenAddresses = useAtomValue(favoritesAtom)
const showFavorites = useAtomValue(showFavoritesAtom) const showFavorites = useAtomValue(showFavoritesAtom)
const shownTokens = showFavorites ? favoriteTokens : addresses const shownTokens =
const allTokens = useAllTokens() showFavorites && tokens ? tokens.filter((token) => favoriteTokenAddresses.includes(token.address)) : tokens
return useMemo( return useMemo(
() => () =>
shownTokens.filter((tokenAddress) => { (shownTokens ?? []).filter((token) => {
const token = allTokens[tokenAddress] if (!token.address) {
const tokenName = token?.name ?? '' return false
const tokenSymbol = token?.symbol ?? '' }
if (!filterString) { if (!filterString) {
return true return true
} }
const lowercaseFilterString = filterString.toLowerCase() const lowercaseFilterString = filterString.toLowerCase()
const addressIncludesFilterString = tokenAddress.toLowerCase().includes(lowercaseFilterString) const addressIncludesFilterString = token?.address?.toLowerCase().includes(lowercaseFilterString)
const nameIncludesFilterString = tokenName.toLowerCase().includes(lowercaseFilterString) const nameIncludesFilterString = token?.name?.toLowerCase().includes(lowercaseFilterString)
const symbolIncludesFilterString = tokenSymbol.toLowerCase().includes(lowercaseFilterString) const symbolIncludesFilterString = token?.symbol?.toLowerCase().includes(lowercaseFilterString)
return nameIncludesFilterString || symbolIncludesFilterString || addressIncludesFilterString return nameIncludesFilterString || symbolIncludesFilterString || addressIncludesFilterString
}), }),
[allTokens, shownTokens, filterString] [shownTokens, filterString]
) )
} }
function useSortedTokens(addresses: string[], tokenData: Record<string, TokenData> | null) { function useSortedTokens(tokenData: TokenData[] | null) {
const sortCategory = useAtomValue(sortCategoryAtom) const sortCategory = useAtomValue(sortCategoryAtom)
const sortDirection = useAtomValue(sortDirectionAtom) const sortDirection = useAtomValue(sortDirectionAtom)
const timePeriod = useAtomValue<TimePeriod>(filterTimeAtom) const timePeriod = useAtomValue<TimePeriod>(filterTimeAtom)
@ -93,40 +91,38 @@ function useSortedTokens(addresses: string[], tokenData: Record<string, TokenDat
return useMemo( return useMemo(
() => () =>
addresses.sort((token1Address, token2Address) => { tokenData &&
tokenData.sort((token1, token2) => {
if (!tokenData) { if (!tokenData) {
return 0 return 0
} }
// fix any, delta property // fix delta/percent change property
const token1 = tokenData[token1Address] as any
const token2 = tokenData[token2Address] as any
if (!token1 || !token2 || !sortDirection || !sortCategory) { if (!token1 || !token2 || !sortDirection || !sortCategory) {
return 0 return 0
} }
let a: number let a: number | null | undefined
let b: number let b: number | null | undefined
switch (sortCategory) { switch (sortCategory) {
case Category.marketCap: case Category.marketCap:
a = token1.marketCap a = token1.marketCap?.value
b = token2.marketCap b = token2.marketCap?.value
break
case Category.percentChange:
a = token1.delta
b = token2.delta
break break
case Category.price: case Category.price:
a = token1.price a = token1.price?.value
b = token2.price b = token2.price?.value
break break
case Category.volume: case Category.volume:
a = token1.volume[timePeriod] a = token1.volume?.[timePeriod]?.value
b = token2.volume[timePeriod] b = token2.volume?.[timePeriod]?.value
break
case Category.percentChange:
a = token1.percentChange?.[timePeriod]?.value
b = token2.percentChange?.[timePeriod]?.value
break break
} }
return sortFn(a, b) return sortFn(a, b)
}), }),
[addresses, tokenData, sortDirection, sortCategory, sortFn, timePeriod] [tokenData, sortDirection, sortCategory, sortFn, timePeriod]
) )
} }
@ -152,12 +148,11 @@ export function LoadingTokenTable() {
) )
} }
export default function TokenTable({ data }: { data: Record<string, TokenData> | null }) { export default function TokenTable({ data }: { data: TokenData[] | undefined }) {
const showFavorites = useAtomValue<boolean>(showFavoritesAtom) const showFavorites = useAtomValue<boolean>(showFavoritesAtom)
const timePeriod = useAtomValue<TimePeriod>(filterTimeAtom) const timePeriod = useAtomValue<TimePeriod>(filterTimeAtom)
const topTokenAddresses = data ? Object.keys(data) : [] const filteredTokens = useFilteredTokens(data)
const filteredTokens = useFilteredTokens(topTokenAddresses) const sortedFilteredTokens = useSortedTokens(filteredTokens)
const filteredAndSortedTokens = useSortedTokens(filteredTokens, data)
/* loading and error state */ /* loading and error state */
if (data === null) { if (data === null) {
@ -173,11 +168,11 @@ export default function TokenTable({ data }: { data: Record<string, TokenData> |
) )
} }
if (showFavorites && filteredAndSortedTokens.length === 0) { if (showFavorites && sortedFilteredTokens?.length === 0) {
return <NoTokensState message="You have no favorited tokens" /> return <NoTokensState message="You have no favorited tokens" />
} }
if (!showFavorites && filteredAndSortedTokens.length === 0) { if (!showFavorites && sortedFilteredTokens?.length === 0) {
return <NoTokensState message="No tokens found" /> return <NoTokensState message="No tokens found" />
} }
@ -186,13 +181,13 @@ export default function TokenTable({ data }: { data: Record<string, TokenData> |
<GridContainer> <GridContainer>
<HeaderRow /> <HeaderRow />
<TokenRowsContainer> <TokenRowsContainer>
{filteredAndSortedTokens.map((tokenAddress, index) => ( {sortedFilteredTokens?.map((token, index) => (
<LoadedRow <LoadedRow
key={tokenAddress} key={token.address}
tokenAddress={tokenAddress} tokenAddress={token.address}
tokenListIndex={index} tokenListIndex={index}
tokenListLength={filteredAndSortedTokens.length} tokenListLength={sortedFilteredTokens.length}
tokenData={data[tokenAddress]} tokenData={token}
timePeriod={timePeriod} timePeriod={timePeriod}
/> />
))} ))}

@ -1,5 +1,5 @@
import { SupportedChainId } from 'constants/chains' import { SupportedChainId } from 'constants/chains'
import { TimePeriod } from 'hooks/useExplorePageQuery' import { TimePeriod } from 'graphql/data/TopTokenQuery'
import { atom, useAtom } from 'jotai' import { atom, useAtom } from 'jotai'
import { atomWithReset, atomWithStorage } from 'jotai/utils' import { atomWithReset, atomWithStorage } from 'jotai/utils'
import { useCallback } from 'react' import { useCallback } from 'react'

@ -1,8 +1,8 @@
import graphql from 'babel-plugin-relay/macro' import graphql from 'babel-plugin-relay/macro'
import { TimePeriod } from 'hooks/useExplorePageQuery'
import { useLazyLoadQuery } from 'react-relay' import { useLazyLoadQuery } from 'react-relay'
import type { Chain, TokenPriceQuery as TokenPriceQueryType } from './__generated__/TokenPriceQuery.graphql' import type { Chain, TokenPriceQuery as TokenPriceQueryType } from './__generated__/TokenPriceQuery.graphql'
import { TimePeriod } from './TopTokenQuery'
export function useTokenPriceQuery(address: string, timePeriod: TimePeriod, chain: Chain) { export function useTokenPriceQuery(address: string, timePeriod: TimePeriod, chain: Chain) {
const tokenPrices = useLazyLoadQuery<TokenPriceQueryType>( const tokenPrices = useLazyLoadQuery<TokenPriceQueryType>(

@ -1,14 +1,44 @@
import graphql from 'babel-plugin-relay/macro' import graphql from 'babel-plugin-relay/macro'
import { TimePeriod, TokenData } from 'hooks/useExplorePageQuery'
import { useLazyLoadQuery } from 'react-relay' import { useLazyLoadQuery } from 'react-relay'
import type { TopTokenQuery as TopTokenQueryType } from './__generated__/TopTokenQuery.graphql' import type { Chain, Currency, TopTokenQuery as TopTokenQueryType } from './__generated__/TopTokenQuery.graphql'
export enum TimePeriod {
HOUR,
DAY,
WEEK,
MONTH,
YEAR,
ALL,
}
interface IAmount {
currency: Currency | null
value: number | null
}
export type TokenData = {
name: string | null
address: string
chain: Chain | null
symbol: string | null
price: IAmount | null | undefined
marketCap: IAmount | null | undefined
volume: Record<TimePeriod, IAmount | null | undefined>
percentChange: Record<TimePeriod, IAmount | null | undefined>
}
export interface UseTopTokensResult {
data: TokenData[] | undefined
error: string | null
loading: boolean
}
export function useTopTokenQuery(page: number) { export function useTopTokenQuery(page: number) {
const topTokenData = useLazyLoadQuery<TopTokenQueryType>( const topTokenData = useLazyLoadQuery<TopTokenQueryType>(
graphql` graphql`
query TopTokenQuery($page: Int!) { query TopTokenQuery($page: Int!) {
topTokenProjects(orderBy: MARKET_CAP, pageSize: 50, currency: USD, page: $page) { topTokenProjects(orderBy: MARKET_CAP, pageSize: 100, currency: USD, page: $page) {
name name
tokens { tokens {
chain chain
@ -85,12 +115,11 @@ export function useTopTokenQuery(page: number) {
} }
) )
const topTokens: Record<string, TokenData> = const topTokens: TokenData[] | undefined = topTokenData.topTokenProjects?.map((token) =>
topTokenData.topTokenProjects?.reduce((acc, token) => { token?.tokens?.[0].address
const tokenAddress = token?.tokens?.[0].address ? {
if (tokenAddress) {
acc[tokenAddress] = {
name: token?.name, name: token?.name,
address: token?.tokens?.[0].address,
chain: token?.tokens?.[0].chain, chain: token?.tokens?.[0].chain,
symbol: token?.tokens?.[0].symbol, symbol: token?.tokens?.[0].symbol,
price: token?.markets?.[0]?.price, price: token?.markets?.[0]?.price,
@ -112,8 +141,7 @@ export function useTopTokenQuery(page: number) {
[TimePeriod.ALL]: token?.markets?.[0]?.pricePercentChangeAll, [TimePeriod.ALL]: token?.markets?.[0]?.pricePercentChangeAll,
}, },
} }
} : ({} as TokenData)
return acc )
}, {} as Record<string, TokenData>) ?? {}
return topTokens return topTokens
} }

@ -1,65 +0,0 @@
import { Chain, Currency } from 'graphql/data/__generated__/TopTokenQuery.graphql'
import { useTopTokenQuery } from 'graphql/data/TopTokenQuery'
import { useEffect, useState } from 'react'
export enum TimePeriod {
HOUR,
DAY,
WEEK,
MONTH,
YEAR,
ALL,
}
interface IAmount {
currency: Currency | null
value: number | null
}
export type TokenData = {
name: string | null | undefined
chain: Chain | undefined
symbol: string | null | undefined
price: IAmount | null | undefined
marketCap: IAmount | null | undefined
volume: Record<TimePeriod, IAmount | null | undefined>
percentChange: Record<TimePeriod, IAmount | null | undefined>
}
export interface UseTopTokensResult {
data: Record<string, TokenData> | null
error: string | null
loading: boolean
}
const useExplorePageQuery = (): UseTopTokensResult => {
const [data, setData] = useState<Record<string, TokenData> | null>(null)
const [error, setError] = useState<string | null>(null)
const [loading, setLoading] = useState(false)
const FetchTopTokens = async (): Promise<Record<string, TokenData> | void> => {
try {
const topTokens = useTopTokenQuery(1)
return topTokens
} catch (e) {
setError('Error fetching top tokens')
} finally {
setLoading(false)
}
}
useEffect(() => {
setError(null)
setLoading(true)
FetchTopTokens()
.then((data) => {
if (data) setData(data)
})
.catch((e) => setError(e))
.finally(() => setLoading(false))
}, [])
return { data, error, loading }
}
export default useExplorePageQuery