feat: polish select (#3160)
* feat: filter selected currency from select * test: use infura urls * fix: load native with chain * fix: use currencyId for key * feat: switch currencies when selecting other * fix: resolve merge conflict name
This commit is contained in:
parent
567fb0181c
commit
b501974a76
@ -20,6 +20,7 @@ import {
|
|||||||
import AutoSizer from 'react-virtualized-auto-sizer'
|
import AutoSizer from 'react-virtualized-auto-sizer'
|
||||||
import { areEqual, FixedSizeList, FixedSizeListProps } from 'react-window'
|
import { areEqual, FixedSizeList, FixedSizeListProps } from 'react-window'
|
||||||
import invariant from 'tiny-invariant'
|
import invariant from 'tiny-invariant'
|
||||||
|
import { currencyId } from 'utils/currencyId'
|
||||||
|
|
||||||
import { BaseButton } from '../Button'
|
import { BaseButton } from '../Button'
|
||||||
import Column from '../Column'
|
import Column from '../Column'
|
||||||
@ -107,10 +108,7 @@ function TokenOption({ index, value, style }: TokenOptionProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemKey = (index: number, tokens: ItemData) => {
|
const itemKey = (index: number, tokens: ItemData) => currencyId(tokens[index])
|
||||||
if (tokens[index].isNative) return 'native'
|
|
||||||
return tokens[index].wrapped.address
|
|
||||||
}
|
|
||||||
const ItemRow = memo(function ItemRow({
|
const ItemRow = memo(function ItemRow({
|
||||||
data: tokens,
|
data: tokens,
|
||||||
index,
|
index,
|
||||||
|
@ -2,7 +2,8 @@ import { t, Trans } from '@lingui/macro'
|
|||||||
import { Currency } from '@uniswap/sdk-core'
|
import { Currency } from '@uniswap/sdk-core'
|
||||||
import { useQueryTokenList } from 'lib/hooks/useTokenList'
|
import { useQueryTokenList } from 'lib/hooks/useTokenList'
|
||||||
import styled, { ThemedText } from 'lib/theme'
|
import styled, { ThemedText } from 'lib/theme'
|
||||||
import { ElementRef, useCallback, useEffect, useRef, useState } from 'react'
|
import { ElementRef, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
import { currencyId } from 'utils/currencyId'
|
||||||
|
|
||||||
import Column from '../Column'
|
import Column from '../Column'
|
||||||
import Dialog, { Header } from '../Dialog'
|
import Dialog, { Header } from '../Dialog'
|
||||||
@ -17,14 +18,18 @@ const SearchInput = styled(StringInput)`
|
|||||||
${inputCss}
|
${inputCss}
|
||||||
`
|
`
|
||||||
|
|
||||||
export function TokenSelectDialog({ onSelect }: { onSelect: (token: Currency) => void }) {
|
interface TokenSelectDialogProps {
|
||||||
|
value?: Currency
|
||||||
|
onSelect: (token: Currency) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TokenSelectDialog({ value, onSelect }: TokenSelectDialogProps) {
|
||||||
const [query, setQuery] = useState('')
|
const [query, setQuery] = useState('')
|
||||||
const tokens = useQueryTokenList(query)
|
const queriedTokens = useQueryTokenList(query)
|
||||||
|
const tokens = useMemo(() => queriedTokens.filter((token) => token !== value), [queriedTokens, value])
|
||||||
|
|
||||||
const baseTokens: Currency[] = [] // TODO(zzmp): Add base tokens to token list functionality
|
const baseTokens: Currency[] = [] // TODO(zzmp): Add base tokens to token list functionality
|
||||||
|
|
||||||
// TODO(zzmp): Disable already selected tokens (passed as props?)
|
|
||||||
|
|
||||||
const input = useRef<HTMLInputElement>(null)
|
const input = useRef<HTMLInputElement>(null)
|
||||||
useEffect(() => input.current?.focus(), [input])
|
useEffect(() => input.current?.focus(), [input])
|
||||||
|
|
||||||
@ -49,7 +54,7 @@ export function TokenSelectDialog({ onSelect }: { onSelect: (token: Currency) =>
|
|||||||
{Boolean(baseTokens.length) && (
|
{Boolean(baseTokens.length) && (
|
||||||
<Row pad={0.75} gap={0.25} justify="flex-start" flex>
|
<Row pad={0.75} gap={0.25} justify="flex-start" flex>
|
||||||
{baseTokens.map((token) => (
|
{baseTokens.map((token) => (
|
||||||
<TokenBase value={token} onClick={onSelect} key={token.wrapped.address} />
|
<TokenBase value={token} onClick={onSelect} key={currencyId(token)} />
|
||||||
))}
|
))}
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
@ -81,7 +86,7 @@ export default function TokenSelect({ value, collapsed, disabled, onSelect }: To
|
|||||||
<TokenButton value={value} collapsed={collapsed} disabled={disabled} onClick={() => setOpen(true)} />
|
<TokenButton value={value} collapsed={collapsed} disabled={disabled} onClick={() => setOpen(true)} />
|
||||||
{open && (
|
{open && (
|
||||||
<Dialog color="module" onClose={() => setOpen(false)}>
|
<Dialog color="module" onClose={() => setOpen(false)}>
|
||||||
<TokenSelectDialog onSelect={selectAndClose} />
|
<TokenSelectDialog value={value} onSelect={selectAndClose} />
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
|
import { INFURA_NETWORK_URLS } from 'constants/chainInfo'
|
||||||
import { SupportedChainId } from 'constants/chains'
|
import { SupportedChainId } from 'constants/chains'
|
||||||
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from 'constants/locales'
|
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from 'constants/locales'
|
||||||
import Widget from 'lib/components/Widget'
|
import Widget from 'lib/components/Widget'
|
||||||
import { darkTheme, defaultTheme, lightTheme } from 'lib/theme'
|
import { darkTheme, defaultTheme, lightTheme } from 'lib/theme'
|
||||||
import { ReactNode, useEffect, useMemo } from 'react'
|
import { ReactNode, useEffect, useMemo } from 'react'
|
||||||
import { useSelect, useValue } from 'react-cosmos/fixture'
|
import { useSelect, useValue } from 'react-cosmos/fixture'
|
||||||
|
import { initializeConnector } from 'widgets-web3-react/core'
|
||||||
|
import { MetaMask } from 'widgets-web3-react/metamask'
|
||||||
|
|
||||||
import { metaMask } from '../connectors/metaMask'
|
export const [metaMask] = initializeConnector<MetaMask>((actions) => new MetaMask(actions))
|
||||||
import { URLS } from '../connectors/network'
|
|
||||||
|
|
||||||
export default function Wrapper({ children }: { children: ReactNode }) {
|
export default function Wrapper({ children }: { children: ReactNode }) {
|
||||||
const [width] = useValue('width', { defaultValue: 360 })
|
const [width] = useValue('width', { defaultValue: 360 })
|
||||||
@ -21,8 +23,8 @@ export default function Wrapper({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
const NO_JSON_RPC = 'None'
|
const NO_JSON_RPC = 'None'
|
||||||
const [jsonRpcEndpoint] = useSelect('JSON-RPC', {
|
const [jsonRpcEndpoint] = useSelect('JSON-RPC', {
|
||||||
defaultValue: URLS[SupportedChainId.MAINNET][0] || NO_JSON_RPC,
|
defaultValue: INFURA_NETWORK_URLS[SupportedChainId.MAINNET],
|
||||||
options: [NO_JSON_RPC, ...Object.values(URLS).flat()],
|
options: [NO_JSON_RPC, ...Object.values(INFURA_NETWORK_URLS).sort()],
|
||||||
})
|
})
|
||||||
|
|
||||||
const NO_PROVIDER = 'None'
|
const NO_PROVIDER = 'None'
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
import { initializeConnector } from 'widgets-web3-react/core'
|
|
||||||
import { MetaMask } from 'widgets-web3-react/metamask'
|
|
||||||
|
|
||||||
export const [metaMask, hooks] = initializeConnector<MetaMask>((actions) => new MetaMask(actions))
|
|
@ -1,14 +0,0 @@
|
|||||||
import { SupportedChainId } from 'constants/chains'
|
|
||||||
|
|
||||||
const ALCHEMY_KEY = '-mzwnEVG3Ssm75WVbmsEpYiekfTF3W1z'
|
|
||||||
const alchemyUrl = (network: string) => `https://${network}.alchemyapi.io/v2/${ALCHEMY_KEY}`
|
|
||||||
|
|
||||||
export const URLS = {
|
|
||||||
[SupportedChainId.MAINNET]: [alchemyUrl('eth-mainnet')],
|
|
||||||
[SupportedChainId.ROPSTEN]: [alchemyUrl('eth-ropsten')],
|
|
||||||
[SupportedChainId.RINKEBY]: [alchemyUrl('eth-rinkeby')],
|
|
||||||
[SupportedChainId.GOERLI]: [alchemyUrl('eth-goerli')],
|
|
||||||
[SupportedChainId.KOVAN]: [alchemyUrl('eth-kovan')],
|
|
||||||
[SupportedChainId.OPTIMISM]: [alchemyUrl('optimism-mainnet')],
|
|
||||||
[SupportedChainId.ARBITRUM_ONE]: [alchemyUrl('arbitrum-mainnet')],
|
|
||||||
}
|
|
@ -6,25 +6,15 @@ import { amountAtom, Field, independentFieldAtom, swapAtom } from 'lib/state/swa
|
|||||||
import { useCallback, useMemo } from 'react'
|
import { useCallback, useMemo } from 'react'
|
||||||
export { default as useSwapInfo } from './useSwapInfo'
|
export { default as useSwapInfo } from './useSwapInfo'
|
||||||
|
|
||||||
export function useSwapCurrency(field: Field): [Currency | undefined, (currency?: Currency) => void] {
|
function otherField(field: Field) {
|
||||||
const atom = useMemo(() => pickAtom(swapAtom, field), [field])
|
switch (field) {
|
||||||
return useAtom(atom)
|
case Field.INPUT:
|
||||||
}
|
return Field.OUTPUT
|
||||||
|
break
|
||||||
export function useSwapAmount(field: Field): [string | undefined, (amount: string) => void] {
|
case Field.OUTPUT:
|
||||||
const amount = useAtomValue(amountAtom)
|
return Field.INPUT
|
||||||
const independentField = useAtomValue(independentFieldAtom)
|
break
|
||||||
const value = useMemo(() => (independentField === field ? amount : undefined), [amount, independentField, field])
|
}
|
||||||
const updateSwap = useUpdateAtom(swapAtom)
|
|
||||||
const updateAmount = useCallback(
|
|
||||||
(amount: string) =>
|
|
||||||
updateSwap((swap) => {
|
|
||||||
swap.independentField = field
|
|
||||||
swap.amount = amount
|
|
||||||
}),
|
|
||||||
[field, updateSwap]
|
|
||||||
)
|
|
||||||
return [value, updateAmount]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSwitchSwapCurrencies() {
|
export function useSwitchSwapCurrencies() {
|
||||||
@ -45,3 +35,38 @@ export function useSwitchSwapCurrencies() {
|
|||||||
})
|
})
|
||||||
}, [update])
|
}, [update])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useSwapCurrency(field: Field): [Currency | undefined, (currency?: Currency) => void] {
|
||||||
|
const atom = useMemo(() => pickAtom(swapAtom, field), [field])
|
||||||
|
const otherAtom = useMemo(() => pickAtom(swapAtom, otherField(field)), [field])
|
||||||
|
const [currency, setCurrency] = useAtom(atom)
|
||||||
|
const otherCurrency = useAtomValue(otherAtom)
|
||||||
|
const switchSwapCurrencies = useSwitchSwapCurrencies()
|
||||||
|
const setOrSwitchCurrency = useCallback(
|
||||||
|
(currency?: Currency) => {
|
||||||
|
if (currency === otherCurrency) {
|
||||||
|
switchSwapCurrencies()
|
||||||
|
} else {
|
||||||
|
setCurrency(currency)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[otherCurrency, setCurrency, switchSwapCurrencies]
|
||||||
|
)
|
||||||
|
return [currency, setOrSwitchCurrency]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSwapAmount(field: Field): [string | undefined, (amount: string) => void] {
|
||||||
|
const amount = useAtomValue(amountAtom)
|
||||||
|
const independentField = useAtomValue(independentFieldAtom)
|
||||||
|
const value = useMemo(() => (independentField === field ? amount : undefined), [amount, independentField, field])
|
||||||
|
const updateSwap = useUpdateAtom(swapAtom)
|
||||||
|
const updateAmount = useCallback(
|
||||||
|
(amount: string) =>
|
||||||
|
updateSwap((swap) => {
|
||||||
|
swap.independentField = field
|
||||||
|
swap.amount = amount
|
||||||
|
}),
|
||||||
|
[field, updateSwap]
|
||||||
|
)
|
||||||
|
return [value, updateAmount]
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
import { nativeOnChain } from 'constants/tokens'
|
||||||
import useDebounce from 'hooks/useDebounce'
|
import useDebounce from 'hooks/useDebounce'
|
||||||
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
|
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
|
||||||
import { useTokenBalances } from 'lib/hooks/useCurrencyBalance'
|
import { useTokenBalances } from 'lib/hooks/useCurrencyBalance'
|
||||||
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ import { getTokenFilter } from './filtering'
|
|||||||
import { tokenComparator, useSortTokensByQuery } from './sorting'
|
import { tokenComparator, useSortTokensByQuery } from './sorting'
|
||||||
|
|
||||||
export function useQueryTokens(query: string, tokens: WrappedTokenInfo[]) {
|
export function useQueryTokens(query: string, tokens: WrappedTokenInfo[]) {
|
||||||
const { account } = useActiveWeb3React()
|
const { chainId, account } = useActiveWeb3React()
|
||||||
const balances = useTokenBalances(account, tokens)
|
const balances = useTokenBalances(account, tokens)
|
||||||
const sortedTokens = useMemo(
|
const sortedTokens = useMemo(
|
||||||
// Create a new array because sort is in-place and returns a referentially equivalent array.
|
// Create a new array because sort is in-place and returns a referentially equivalent array.
|
||||||
@ -23,7 +23,7 @@ export function useQueryTokens(query: string, tokens: WrappedTokenInfo[]) {
|
|||||||
|
|
||||||
const queriedTokens = useSortTokensByQuery(debouncedQuery, filteredTokens)
|
const queriedTokens = useSortTokensByQuery(debouncedQuery, filteredTokens)
|
||||||
|
|
||||||
const native = useNativeCurrency()
|
const native = useMemo(() => chainId && nativeOnChain(chainId), [chainId])
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
if (native && filter(native)) {
|
if (native && filter(native)) {
|
||||||
return [native, ...queriedTokens]
|
return [native, ...queriedTokens]
|
||||||
|
Loading…
Reference in New Issue
Block a user