uniswap-interface-uncensored/src/hooks/index.ts

261 lines
7.3 KiB
TypeScript
Raw Normal View History

Refactor ExchangePage into Swap and Send pages (#774) * Part 1, separate swap and send, move duplicate code into separate files * Move some more constants out of the swap/send * Support defaults from URL parameters * Implement query string parsing via a redux action * Finish merging the changes * Fix the slippage warnings * Clean up some other files * More refactoring * Move the price bar out of the component * Move advanced details and some computations into utilities * Make same improvements to send * Approval hook * Swap page functional with swap callback hook * Swap/send page functional with swap callback hook * Fix lint * Move styleds.ts * Fix integration test * Fix error state in swap and move some things around * Move send callback out of send page * Make send behave more like current behavior * Differentiate swap w/o send on send page from swap w/o send on swap page * Remove userAdvanced which was always false * Remove the price bar which is not used in the new UI * Delete the file * Fix null in the send dialog and move another component out of send * Move the swap modal header out to another file * Include change after merge * Add recipient to swap message * Keep input token selected if user has no balance and clicks send with swap * Move the modal footer out of send component * Remove the hard coded estimated time * Fix the label/action for swap/sends * Share the swap modal footer between swap and send * Fix integration test * remove margin from popper to suppress warnings fix missing ENS name recipient link default deadline to 15 minutes * ensure useApproveCallback returns accurate data * clean up callbacks * extra space * Re-apply ignored changes from v2 branch ExchangePage file Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2020-05-17 00:55:22 +03:00
import { Contract } from '@ethersproject/contracts'
import { Web3Provider } from '@ethersproject/providers'
import { useState, useMemo, useCallback, useEffect, useRef } from 'react'
import { useWeb3React as useWeb3ReactCore } from '@web3-react/core'
import { isMobile } from 'react-device-detect'
2020-03-20 23:03:23 +03:00
import copy from 'copy-to-clipboard'
import ERC20_ABI from '../constants/abis/erc20.json'
import { injected } from '../connectors'
2020-03-20 23:03:23 +03:00
import { NetworkContextName } from '../constants'
2020-05-05 23:36:22 +03:00
import { getContract, getExchangeContract, isAddress } from '../utils'
export function useActiveWeb3React() {
const context = useWeb3ReactCore<Web3Provider>()
const contextNetwork = useWeb3ReactCore<Web3Provider>(NetworkContextName)
return context.active ? context : contextNetwork
}
export function useEagerConnect() {
const { activate, active } = useWeb3ReactCore() // specifically using useWeb3ReactCore because of what this hook does
const [tried, setTried] = useState(false)
useEffect(() => {
injected.isAuthorized().then(isAuthorized => {
if (isAuthorized) {
activate(injected, undefined, true).catch(() => {
setTried(true)
})
} else {
2020-05-09 01:32:36 +03:00
if (isMobile && window.ethereum) {
activate(injected, undefined, true).catch(() => {
setTried(true)
})
} else {
setTried(true)
}
}
})
}, [activate]) // intentionally only running on mount (make sure it's only mounted once :))
// if the connection worked, wait until we get confirmation of that to flip the flag
useEffect(() => {
if (active) {
setTried(true)
}
}, [active])
return tried
}
/**
* Use for network and injected - logs user in
* and out after checking what network theyre on
*/
export function useInactiveListener(suppress = false) {
const { active, error, activate } = useWeb3ReactCore() // specifically using useWeb3React because of what this hook does
useEffect(() => {
2020-05-09 01:32:36 +03:00
const { ethereum } = window
if (ethereum && ethereum.on && !active && !error && !suppress) {
const handleChainChanged = () => {
// eat errors
2020-05-09 01:32:36 +03:00
activate(injected, undefined, true).catch(error => {
console.log(error)
})
}
const handleAccountsChanged = accounts => {
if (accounts.length > 0) {
// eat errors
2020-05-09 01:32:36 +03:00
activate(injected, undefined, true).catch(error => {
console.log(error)
})
}
}
const handleNetworkChanged = () => {
// eat errors
2020-05-09 01:32:36 +03:00
activate(injected, undefined, true).catch(error => {
console.log(error)
})
}
ethereum.on('chainChanged', handleChainChanged)
ethereum.on('networkChanged', handleNetworkChanged)
ethereum.on('accountsChanged', handleAccountsChanged)
return () => {
if (ethereum.removeListener) {
ethereum.removeListener('chainChanged', handleChainChanged)
ethereum.removeListener('networkChanged', handleNetworkChanged)
ethereum.removeListener('accountsChanged', handleAccountsChanged)
}
}
}
}, [active, error, suppress, activate])
}
// modified from https://usehooks.com/useDebounce/
export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value)
useEffect(() => {
// Update debounced value after delay
const handler = setTimeout(() => {
setDebouncedValue(value)
}, delay)
// Cancel the timeout if value changes (also on delay change or unmount)
// This is how we prevent debounced value from updating if value is changed ...
// .. within the delay period. Timeout gets cleared and restarted.
return () => {
clearTimeout(handler)
}
}, [value, delay])
return debouncedValue
}
// modified from https://usehooks.com/useKeyPress/
export function useBodyKeyDown(targetKey, onKeyDown, suppressOnKeyDown = false) {
const downHandler = useCallback(
event => {
const {
target: { tagName },
key
} = event
if (key === targetKey && tagName === 'BODY' && !suppressOnKeyDown) {
event.preventDefault()
onKeyDown()
}
},
[targetKey, onKeyDown, suppressOnKeyDown]
)
useEffect(() => {
window.addEventListener('keydown', downHandler)
return () => {
window.removeEventListener('keydown', downHandler)
}
}, [downHandler])
}
export function useENSName(address) {
const { library } = useActiveWeb3React()
const [ENSName, setENSName] = useState<string | null>(null)
useEffect(() => {
if (isAddress(address)) {
let stale = false
library
.lookupAddress(address)
.then(name => {
if (!stale) {
if (name) {
2019-10-22 01:30:47 +03:00
setENSName(name)
} else {
2019-10-22 01:30:47 +03:00
setENSName(null)
}
}
})
.catch(() => {
if (!stale) {
setENSName(null)
}
})
return () => {
stale = true
setENSName(null)
}
}
}, [library, address])
return ENSName
}
// returns null on errors
export function useContract(address, ABI, withSignerIfPossible = true) {
const { library, account } = useActiveWeb3React()
return useMemo(() => {
try {
return getContract(address, ABI, library, withSignerIfPossible ? account : undefined)
} catch {
return null
}
}, [address, ABI, library, withSignerIfPossible, account])
}
// returns null on errors
Refactor ExchangePage into Swap and Send pages (#774) * Part 1, separate swap and send, move duplicate code into separate files * Move some more constants out of the swap/send * Support defaults from URL parameters * Implement query string parsing via a redux action * Finish merging the changes * Fix the slippage warnings * Clean up some other files * More refactoring * Move the price bar out of the component * Move advanced details and some computations into utilities * Make same improvements to send * Approval hook * Swap page functional with swap callback hook * Swap/send page functional with swap callback hook * Fix lint * Move styleds.ts * Fix integration test * Fix error state in swap and move some things around * Move send callback out of send page * Make send behave more like current behavior * Differentiate swap w/o send on send page from swap w/o send on swap page * Remove userAdvanced which was always false * Remove the price bar which is not used in the new UI * Delete the file * Fix null in the send dialog and move another component out of send * Move the swap modal header out to another file * Include change after merge * Add recipient to swap message * Keep input token selected if user has no balance and clicks send with swap * Move the modal footer out of send component * Remove the hard coded estimated time * Fix the label/action for swap/sends * Share the swap modal footer between swap and send * Fix integration test * remove margin from popper to suppress warnings fix missing ENS name recipient link default deadline to 15 minutes * ensure useApproveCallback returns accurate data * clean up callbacks * extra space * Re-apply ignored changes from v2 branch ExchangePage file Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2020-05-17 00:55:22 +03:00
export function useTokenContract(tokenAddress: string, withSignerIfPossible = true): Contract {
const { library, account } = useActiveWeb3React()
return useMemo(() => {
try {
return getContract(tokenAddress, ERC20_ABI, library, withSignerIfPossible ? account : undefined)
} catch {
return null
}
}, [tokenAddress, library, withSignerIfPossible, account])
}
export function usePairContract(pairAddress, withSignerIfPossible = true) {
const { library, account } = useActiveWeb3React()
return useMemo(() => {
try {
return getExchangeContract(pairAddress, library, withSignerIfPossible ? account : undefined)
} catch {
return null
}
}, [pairAddress, library, withSignerIfPossible, account])
}
export function useCopyClipboard(timeout = 500): [boolean, (toCopy: string) => void] {
const [isCopied, setIsCopied] = useState(false)
const staticCopy = useCallback(text => {
const didCopy = copy(text)
setIsCopied(didCopy)
}, [])
useEffect(() => {
if (isCopied) {
const hide = setTimeout(() => {
setIsCopied(false)
}, timeout)
return () => {
clearTimeout(hide)
}
}
}, [isCopied, setIsCopied, timeout])
return [isCopied, staticCopy]
}
// modified from https://usehooks.com/usePrevious/
export function usePrevious(value) {
// The ref object is a generic container whose current property is mutable ...
// ... and can hold any value, similar to an instance property on a class
const ref = useRef()
// Store current value in ref
useEffect(() => {
ref.current = value
}, [value]) // Only re-run if value changes
// Return previous value (happens before update in useEffect above)
return ref.current
}
2020-03-11 17:44:39 +03:00
export function useToggle(initialState = false): [boolean, () => void] {
2020-03-11 17:44:39 +03:00
const [state, setState] = useState(initialState)
const toggle = useCallback(() => setState(state => !state), [])
return [state, toggle]
}