-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
188 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import styled, { ThemedText } from 'lib/theme' | ||
|
||
import Column from '../Column' | ||
import Row from '../Row' | ||
|
||
const Img = styled.div` | ||
clip-path: circle(50%); | ||
height: 1.5em; | ||
width: 1.5em; | ||
` | ||
const Symbol = styled.div` | ||
height: 0.75em; | ||
width: 7em; | ||
` | ||
const Name = styled.div` | ||
height: 0.5em; | ||
width: 5.5em; | ||
` | ||
const Balance = styled.div` | ||
padding: 0.375em 0; | ||
width: 1.5em; | ||
` | ||
const TokenRow = styled.div` | ||
outline: none; | ||
padding: 0.6875em 0.75em; | ||
|
||
${Img}, ${Symbol}, ${Name}, ${Balance} { | ||
background-color: ${({ theme }) => theme.secondary}; | ||
border-radius: 0.25em; | ||
} | ||
` | ||
|
||
function TokenOption() { | ||
return ( | ||
<TokenRow> | ||
<ThemedText.Body1> | ||
<Row> | ||
<Row gap={0.5}> | ||
<Img /> | ||
<Column flex gap={0.125} align="flex-start" justify="flex-center"> | ||
<ThemedText.Subhead1 style={{ display: 'flex' }}> | ||
<Symbol /> | ||
</ThemedText.Subhead1> | ||
<ThemedText.Caption style={{ display: 'flex' }}> | ||
<Name /> | ||
</ThemedText.Caption> | ||
</Column> | ||
</Row> | ||
<Balance /> | ||
</Row> | ||
</ThemedText.Body1> | ||
</TokenRow> | ||
) | ||
} | ||
|
||
export default function TokenOptionsSkeleton() { | ||
return ( | ||
<Column> | ||
<TokenOption /> | ||
<TokenOption /> | ||
<TokenOption /> | ||
<TokenOption /> | ||
<TokenOption /> | ||
</Column> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,64 +1,98 @@ | ||
import { Token } from '@uniswap/sdk-core' | ||
import { NativeCurrency, Token } from '@uniswap/sdk-core' | ||
import { TokenInfo, TokenList } from '@uniswap/token-lists' | ||
import { atom, useAtom } from 'jotai' | ||
import { useAtomValue } from 'jotai/utils' | ||
import { atom } from 'jotai' | ||
import { useAtomValue, useUpdateAtom } from 'jotai/utils' | ||
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React' | ||
import resolveENSContentHash from 'lib/utils/resolveENSContentHash' | ||
import { useEffect, useMemo, useState } from 'react' | ||
import { useCallback, useEffect, useMemo, useState } from 'react' | ||
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo' | ||
|
||
import fetchTokenList from './fetchTokenList' | ||
import { useQueryTokens } from './querying' | ||
import { ChainTokenMap, tokensToChainTokenMap } from './utils' | ||
import { validateTokens } from './validateTokenList' | ||
|
||
export { DEFAULT_TOKEN_LIST } from './fetchTokenList' | ||
export const DEFAULT_TOKEN_LIST = 'https://gateway.ipfs.io/ipns/tokens.uniswap.org' | ||
|
||
const chainTokenMapAtom = atom<ChainTokenMap>({}) | ||
const chainTokenMapAtom = atom<ChainTokenMap | undefined>(undefined) | ||
|
||
export default function useTokenList(list?: string | TokenInfo[]): WrappedTokenInfo[] { | ||
export function useIsTokenListLoaded() { | ||
return Boolean(useAtomValue(chainTokenMapAtom)) | ||
} | ||
|
||
export function useSyncTokenList(list: string | TokenInfo[] = DEFAULT_TOKEN_LIST): void { | ||
const { chainId, library } = useActiveWeb3React() | ||
const [chainTokenMap, setChainTokenMap] = useAtom(chainTokenMapAtom) | ||
const setChainTokenMap = useUpdateAtom(chainTokenMapAtom) | ||
|
||
// Error boundaries will not catch (non-rendering) async errors, but it should still be shown | ||
const [error, setError] = useState<Error>() | ||
if (error) throw error | ||
|
||
const resolver = useCallback( | ||
(ensName: string) => { | ||
if (library && chainId === 1) { | ||
// TODO(zzmp): Use network resolver when wallet is not on chainId === 1. | ||
return resolveENSContentHash(ensName, library) | ||
} | ||
throw new Error('Could not construct mainnet ENS resolver') | ||
}, | ||
[chainId, library] | ||
) | ||
useEffect(() => { | ||
if (list !== undefined) { | ||
let tokens: Promise<TokenList | TokenInfo[]> | ||
if (typeof list === 'string') { | ||
tokens = fetchTokenList(list, (ensName: string) => { | ||
if (library && chainId === 1) { | ||
return resolveENSContentHash(ensName, library) | ||
} | ||
throw new Error('Could not construct mainnet ENS resolver') | ||
}) | ||
} else { | ||
tokens = validateTokens(list) | ||
let stale = false | ||
activateList(list) | ||
return () => { | ||
stale = true | ||
} | ||
|
||
async function activateList(list: string | TokenInfo[]) { | ||
try { | ||
let tokens: TokenList | TokenInfo[] | ||
if (typeof list === 'string') { | ||
tokens = await fetchTokenList(list, resolver) | ||
} else { | ||
tokens = await validateTokens(list) | ||
} | ||
const tokenMap = tokensToChainTokenMap(tokens) // also caches the fetched tokens, so it is invoked even if stale | ||
if (!stale) { | ||
setChainTokenMap(tokenMap) | ||
setError(undefined) | ||
} | ||
} catch (e: unknown) { | ||
if (!stale) { | ||
setChainTokenMap(undefined) | ||
setError(e as Error) | ||
} | ||
} | ||
tokens.then(tokensToChainTokenMap).then(setChainTokenMap).catch(setError) | ||
} | ||
}, [chainId, library, list, setChainTokenMap]) | ||
}, [list, resolver, setChainTokenMap]) | ||
} | ||
|
||
export default function useTokenList(): WrappedTokenInfo[] { | ||
const { chainId } = useActiveWeb3React() | ||
const chainTokenMap = useAtomValue(chainTokenMapAtom) | ||
const tokenMap = chainId && chainTokenMap?.[chainId] | ||
return useMemo(() => { | ||
return Object.values((chainId && chainTokenMap[chainId]) || {}).map(({ token }) => token) | ||
}, [chainId, chainTokenMap]) | ||
if (!tokenMap) return [] | ||
return Object.values(tokenMap).map(({ token }) => token) | ||
}, [tokenMap]) | ||
} | ||
|
||
export type TokenMap = { [address: string]: Token } | ||
|
||
export function useTokenMap(): TokenMap { | ||
const { chainId } = useActiveWeb3React() | ||
const chainTokenMap = useAtomValue(chainTokenMapAtom) | ||
const tokenMap = chainId && chainTokenMap?.[chainId] | ||
return useMemo(() => { | ||
return Object.entries((chainId && chainTokenMap[chainId]) || {}).reduce((map, [address, { token }]) => { | ||
if (!tokenMap) return {} | ||
return Object.entries(tokenMap).reduce((map, [address, { token }]) => { | ||
map[address] = token | ||
return map | ||
}, {} as TokenMap) | ||
}, [chainId, chainTokenMap]) | ||
}, [tokenMap]) | ||
} | ||
|
||
export function useQueryTokenList(query: string) { | ||
export function useQueryCurrencies(query = ''): (WrappedTokenInfo | NativeCurrency)[] { | ||
return useQueryTokens(query, useTokenList()) | ||
} |