Introduce redux for one of the state stores (#742)
* Introduce redux for one of the state stores * Remove unused state * Clean up hooks * Add types for react-redux and fix error from any type on useSelector * Strongly type the web3 provider * Make the popup content into a POJO * Lint errors * Clean up method call * Fix lint error * Fix lint error * Lint
This commit is contained in:
parent
ea015d16f2
commit
6da8e2c84d
3
.gitignore
vendored
3
.gitignore
vendored
@ -30,4 +30,5 @@ notes.txt
|
||||
package-lock.json
|
||||
|
||||
cypress/videos
|
||||
cypress/screenshots
|
||||
cypress/screenshots
|
||||
cypress/fixtures/example.json
|
@ -15,6 +15,7 @@
|
||||
"@material-ui/core": "^4.9.5",
|
||||
"@reach/dialog": "^0.2.8",
|
||||
"@reach/tooltip": "^0.2.0",
|
||||
"@reduxjs/toolkit": "^1.3.5",
|
||||
"@uniswap/sdk": "^2.0.3",
|
||||
"@uniswap/v2-core": "1.0.0",
|
||||
"@uniswap/v2-periphery": "1.0.0-beta.0",
|
||||
@ -41,6 +42,7 @@
|
||||
"react-feather": "^2.0.8",
|
||||
"react-ga": "^2.5.7",
|
||||
"react-i18next": "^10.7.0",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-router-dom": "^5.0.0",
|
||||
"react-scripts": "^3.4.1",
|
||||
"react-spring": "^8.0.27",
|
||||
@ -55,6 +57,7 @@
|
||||
"@types/node": "^13.13.5",
|
||||
"@types/react": "^16.9.34",
|
||||
"@types/react-dom": "^16.9.7",
|
||||
"@types/react-redux": "^7.1.8",
|
||||
"@types/react-router-dom": "^5.0.0",
|
||||
"@types/rebass": "^4.0.5",
|
||||
"@types/styled-components": "^4.2.0",
|
||||
|
@ -7,6 +7,7 @@ import { withRouter, RouteComponentProps } from 'react-router-dom'
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { MaxUint256 } from '@ethersproject/constants'
|
||||
import { Contract } from '@ethersproject/contracts'
|
||||
import { useUserAdvanced } from '../../state/application/hooks'
|
||||
import { Field, SwapAction, useSwapStateReducer } from './swap-store'
|
||||
import { Text } from 'rebass'
|
||||
import Card, { BlueCard, GreyCard, YellowCard } from '../../components/Card'
|
||||
@ -14,7 +15,6 @@ import { AutoColumn, ColumnCenter } from '../../components/Column'
|
||||
import { AutoRow, RowBetween, RowFixed } from '../Row'
|
||||
import { ROUTER_ADDRESS } from '../../constants'
|
||||
import { useTokenAllowance } from '../../data/Allowances'
|
||||
import { useUserAdvanced } from '../../contexts/Application'
|
||||
import { useAddressBalance, useAllBalances } from '../../contexts/Balances'
|
||||
import { useLocalStorageTokens } from '../../contexts/LocalStorage'
|
||||
import { usePair } from '../../data/Reserves'
|
||||
|
@ -3,8 +3,14 @@ import styled, { ThemeContext } from 'styled-components'
|
||||
import { useMediaLayout } from 'use-media'
|
||||
|
||||
import { X } from 'react-feather'
|
||||
import { usePopups } from '../../contexts/Application'
|
||||
import { PopupContent } from '../../state/application/actions'
|
||||
import { usePopups } from '../../state/application/hooks'
|
||||
import { Link } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
import DoubleTokenLogo from '../DoubleLogo'
|
||||
import Row from '../Row'
|
||||
import TxnPopup from '../TxnPopup'
|
||||
import { Text } from 'rebass'
|
||||
|
||||
const StyledClose = styled(X)`
|
||||
position: absolute;
|
||||
@ -66,6 +72,33 @@ const Popup = styled.div`
|
||||
`}
|
||||
`
|
||||
|
||||
function PopupItem({ content, popKey }: { content: PopupContent; popKey: string }) {
|
||||
if ('txn' in content) {
|
||||
const {
|
||||
txn: { hash, success, summary }
|
||||
} = content
|
||||
return <TxnPopup popKey={popKey} hash={hash} success={success} summary={summary} />
|
||||
} else if ('poolAdded' in content) {
|
||||
const {
|
||||
poolAdded: { token0, token1 }
|
||||
} = content
|
||||
return (
|
||||
<AutoColumn gap={'10px'}>
|
||||
<Text fontSize={20} fontWeight={500}>
|
||||
Pool Imported
|
||||
</Text>
|
||||
<Row>
|
||||
<DoubleTokenLogo a0={token0?.address ?? ''} a1={token1?.address ?? ''} margin={true} />
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
UNI {token0?.symbol} / {token1?.symbol}
|
||||
</Text>
|
||||
</Row>
|
||||
<Link>View on Uniswap Info.</Link>
|
||||
</AutoColumn>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const theme = useContext(ThemeContext)
|
||||
// get all popups
|
||||
@ -81,7 +114,7 @@ export default function App() {
|
||||
return (
|
||||
<Popup key={item.key}>
|
||||
<StyledClose color={theme.text2} onClick={() => removePopup(item.key)} />
|
||||
{React.cloneElement(item.content, { popKey: item.key })}
|
||||
<PopupItem content={item.content} popKey={item.key} />
|
||||
</Popup>
|
||||
)
|
||||
})}
|
||||
@ -100,7 +133,7 @@ export default function App() {
|
||||
return (
|
||||
<Popup key={item.key}>
|
||||
<StyledClose color={theme.text2} onClick={() => removePopup(item.key)} />
|
||||
{React.cloneElement(item.content, { popKey: item.key })}
|
||||
<PopupItem content={item.content} popKey={item.key} />
|
||||
</Popup>
|
||||
)
|
||||
})}
|
||||
|
@ -3,10 +3,10 @@ import React, { useState } from 'react'
|
||||
import { AlertCircle, CheckCircle } from 'react-feather'
|
||||
|
||||
import styled from 'styled-components'
|
||||
import { usePopups } from '../../contexts/Application'
|
||||
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import useInterval from '../../hooks/useInterval'
|
||||
import { usePopups } from '../../state/application/hooks'
|
||||
import { TYPE } from '../../theme'
|
||||
|
||||
import { Link } from '../../theme/components'
|
||||
@ -35,7 +35,7 @@ export default function TxnPopup({
|
||||
hash: string
|
||||
success?: boolean
|
||||
summary?: string
|
||||
popKey?: number
|
||||
popKey?: string
|
||||
}) {
|
||||
const { chainId } = useWeb3React()
|
||||
const [count, setCount] = useState(1)
|
||||
|
@ -4,6 +4,7 @@ import styled from 'styled-components'
|
||||
import { isMobile } from 'react-device-detect'
|
||||
import { useWeb3React, UnsupportedChainIdError } from '@web3-react/core'
|
||||
import { URI_AVAILABLE } from '@web3-react/walletconnect-connector'
|
||||
import { useWalletModalOpen, useWalletModalToggle } from '../../state/application/hooks'
|
||||
|
||||
import Modal from '../Modal'
|
||||
import AccountDetails from '../AccountDetails'
|
||||
@ -15,7 +16,6 @@ import { Link } from '../../theme'
|
||||
import MetamaskIcon from '../../assets/images/metamask.png'
|
||||
import { ReactComponent as Close } from '../../assets/images/x.svg'
|
||||
import { injected, walletconnect, fortmatic, portis } from '../../connectors'
|
||||
import { useWalletModalToggle, useWalletModalOpen } from '../../contexts/Application'
|
||||
import { OVERLAY_READY } from '../../connectors/Fortmatic'
|
||||
|
||||
const CloseIcon = styled.div`
|
||||
|
@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import { useWeb3React, UnsupportedChainIdError } from '@web3-react/core'
|
||||
import { darken, lighten } from 'polished'
|
||||
import { Activity } from 'react-feather'
|
||||
import { useWalletModalToggle } from '../../state/application/hooks'
|
||||
|
||||
import Identicon from '../Identicon'
|
||||
import PortisIcon from '../../assets/images/portisIcon.png'
|
||||
@ -21,7 +22,6 @@ import { useENSName } from '../../hooks'
|
||||
import { shortenAddress } from '../../utils'
|
||||
import { useAllTransactions } from '../../contexts/Transactions'
|
||||
import { NetworkContextName } from '../../constants'
|
||||
import { useWalletModalToggle } from '../../contexts/Application'
|
||||
import { injected, walletconnect, walletlink, fortmatic, portis } from '../../connectors'
|
||||
|
||||
const SpinnerWrapper = styled(Spinner)`
|
||||
|
@ -1,226 +0,0 @@
|
||||
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
|
||||
|
||||
import { useWeb3React } from '../hooks'
|
||||
|
||||
const BLOCK_NUMBER = 'BLOCK_NUMBER'
|
||||
const USD_PRICE = 'USD_PRICE'
|
||||
const WALLET_MODAL_OPEN = 'WALLET_MODAL_OPEN'
|
||||
const POPUP_LIST = 'POPUP_LIST'
|
||||
const POPUP_KEY = 'POPUP_KEY'
|
||||
|
||||
const UPDATE_BLOCK_NUMBER = 'UPDATE_BLOCK_NUMBER'
|
||||
const TOGGLE_WALLET_MODAL = 'TOGGLE_WALLET_MODAL'
|
||||
|
||||
const ADD_POPUP = 'ADD_POPUP'
|
||||
const USER_ADVANCED = 'USER_ADVANCED'
|
||||
const TOGGLE_USER_ADVANCED = 'TOGGLE_USER_ADVANCED'
|
||||
|
||||
interface ApplicationState {
|
||||
BLOCK_NUMBER: {}
|
||||
USD_PRICE: {}
|
||||
POPUP_LIST: Array<{ key: number; show: boolean; content: React.ReactElement }>
|
||||
POPUP_KEY: number
|
||||
WALLET_MODAL_OPEN: boolean
|
||||
USER_ADVANCED: boolean
|
||||
}
|
||||
|
||||
const ApplicationContext = createContext<[ApplicationState, { [updater: string]: (...args: any[]) => void }]>([
|
||||
{
|
||||
[BLOCK_NUMBER]: {},
|
||||
[USD_PRICE]: {},
|
||||
[POPUP_LIST]: [],
|
||||
[POPUP_KEY]: 0,
|
||||
[WALLET_MODAL_OPEN]: false,
|
||||
[USER_ADVANCED]: false
|
||||
},
|
||||
{}
|
||||
])
|
||||
|
||||
function useApplicationContext() {
|
||||
return useContext(ApplicationContext)
|
||||
}
|
||||
|
||||
function reducer(state: ApplicationState, { type, payload }): ApplicationState {
|
||||
switch (type) {
|
||||
case UPDATE_BLOCK_NUMBER: {
|
||||
const { networkId, blockNumber } = payload
|
||||
return {
|
||||
...state,
|
||||
[BLOCK_NUMBER]: {
|
||||
...state?.[BLOCK_NUMBER],
|
||||
[networkId]: blockNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case TOGGLE_WALLET_MODAL: {
|
||||
return { ...state, [WALLET_MODAL_OPEN]: !state[WALLET_MODAL_OPEN] }
|
||||
}
|
||||
|
||||
case TOGGLE_USER_ADVANCED: {
|
||||
return { ...state, [USER_ADVANCED]: !state[USER_ADVANCED] }
|
||||
}
|
||||
|
||||
case ADD_POPUP: {
|
||||
const { newList } = payload
|
||||
return { ...state, [POPUP_LIST]: newList, [POPUP_KEY]: state?.[POPUP_KEY] + 1 }
|
||||
}
|
||||
|
||||
default: {
|
||||
throw Error(`Unexpected action type in ApplicationContext reducer: '${type}'.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function Provider({ children }: { children: React.ReactNode }) {
|
||||
const [state, dispatch] = useReducer(reducer, {
|
||||
[BLOCK_NUMBER]: {},
|
||||
[USD_PRICE]: {},
|
||||
[POPUP_LIST]: [],
|
||||
[POPUP_KEY]: 0,
|
||||
[WALLET_MODAL_OPEN]: false,
|
||||
[USER_ADVANCED]: false
|
||||
})
|
||||
|
||||
const updateBlockNumber = useCallback(
|
||||
(networkId, blockNumber) => {
|
||||
dispatch({ type: UPDATE_BLOCK_NUMBER, payload: { networkId, blockNumber } })
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
const toggleWalletModal = useCallback(() => {
|
||||
dispatch({ type: TOGGLE_WALLET_MODAL, payload: null })
|
||||
}, [dispatch])
|
||||
|
||||
const toggleUserAdvanced = useCallback(() => {
|
||||
dispatch({ type: TOGGLE_USER_ADVANCED, payload: null })
|
||||
}, [dispatch])
|
||||
|
||||
const setPopups = useCallback(
|
||||
newList => {
|
||||
dispatch({ type: ADD_POPUP, payload: { newList } })
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
return (
|
||||
<ApplicationContext.Provider
|
||||
value={useMemo(() => [state, { updateBlockNumber, toggleWalletModal, toggleUserAdvanced, setPopups }], [
|
||||
state,
|
||||
updateBlockNumber,
|
||||
toggleWalletModal,
|
||||
toggleUserAdvanced,
|
||||
setPopups
|
||||
])}
|
||||
>
|
||||
{children}
|
||||
</ApplicationContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function Updater() {
|
||||
const { library, chainId } = useWeb3React()
|
||||
|
||||
const [, { updateBlockNumber }] = useApplicationContext()
|
||||
|
||||
// update block number
|
||||
useEffect(() => {
|
||||
if (library) {
|
||||
let stale = false
|
||||
|
||||
const update = () => {
|
||||
library
|
||||
.getBlockNumber()
|
||||
.then(blockNumber => {
|
||||
if (!stale) {
|
||||
updateBlockNumber(chainId, blockNumber)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!stale) {
|
||||
updateBlockNumber(chainId, null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
update()
|
||||
library.on('block', update)
|
||||
|
||||
return () => {
|
||||
stale = true
|
||||
library.removeListener('block', update)
|
||||
}
|
||||
}
|
||||
}, [chainId, library, updateBlockNumber])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function useBlockNumber() {
|
||||
const { chainId } = useWeb3React()
|
||||
|
||||
const [state] = useApplicationContext()
|
||||
|
||||
return state?.[BLOCK_NUMBER]?.[chainId]
|
||||
}
|
||||
|
||||
export function useWalletModalOpen() {
|
||||
const [state] = useApplicationContext()
|
||||
|
||||
return state[WALLET_MODAL_OPEN]
|
||||
}
|
||||
|
||||
export function useWalletModalToggle() {
|
||||
const [, { toggleWalletModal }] = useApplicationContext()
|
||||
|
||||
return toggleWalletModal
|
||||
}
|
||||
|
||||
export function useUserAdvanced() {
|
||||
const [state] = useApplicationContext()
|
||||
|
||||
return state[USER_ADVANCED]
|
||||
}
|
||||
|
||||
export function useToggleUserAdvanced() {
|
||||
const [, { toggleUserAdvanced }] = useApplicationContext()
|
||||
return toggleUserAdvanced
|
||||
}
|
||||
|
||||
export function usePopups(): [
|
||||
ApplicationState['POPUP_LIST'],
|
||||
(content: React.ReactElement) => void,
|
||||
(key: number) => void
|
||||
] {
|
||||
const [state, { setPopups }] = useApplicationContext()
|
||||
|
||||
const index = state[POPUP_KEY]
|
||||
const currentPopups = state[POPUP_LIST]
|
||||
|
||||
function addPopup(content: React.ReactElement): void {
|
||||
const newItem = {
|
||||
show: true,
|
||||
key: index,
|
||||
content: content
|
||||
}
|
||||
currentPopups.push(newItem)
|
||||
setPopups(currentPopups)
|
||||
}
|
||||
|
||||
function removePopup(key: number): void {
|
||||
currentPopups.map(item => {
|
||||
if (key === item.key) {
|
||||
item.show = false
|
||||
}
|
||||
return true
|
||||
})
|
||||
setPopups(currentPopups)
|
||||
}
|
||||
|
||||
const activePopups = currentPopups.filter(item => {
|
||||
return item.show
|
||||
})
|
||||
|
||||
return [activePopups, addPopup, removePopup]
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import React, { createContext, useContext, useReducer, useRef, useMemo, useCallback, useEffect, ReactNode } from 'react'
|
||||
import { TokenAmount, Token, JSBI, WETH } from '@uniswap/sdk'
|
||||
import { useBlockNumber } from '../state/application/hooks'
|
||||
|
||||
import { useAllTokens } from './Tokens'
|
||||
import { useBlockNumber } from './Application'
|
||||
import { useWeb3React, useDebounce } from '../hooks'
|
||||
|
||||
import { getEtherBalance, getTokenBalance, isAddress } from '../utils'
|
||||
@ -158,28 +158,46 @@ function useBalancesContext() {
|
||||
export default function Provider({ children }: { children: ReactNode }) {
|
||||
const [state, dispatch] = useReducer(reducer, undefined, initialize)
|
||||
|
||||
const startListening = useCallback((chainId, address, tokenAddress) => {
|
||||
dispatch({ type: Action.START_LISTENING, payload: { chainId, address, tokenAddress } })
|
||||
}, [])
|
||||
const startListening = useCallback(
|
||||
(chainId, address, tokenAddress) => {
|
||||
dispatch({ type: Action.START_LISTENING, payload: { chainId, address, tokenAddress } })
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
const stopListening = useCallback((chainId, address, tokenAddress) => {
|
||||
dispatch({ type: Action.STOP_LISTENING, payload: { chainId, address, tokenAddress } })
|
||||
}, [])
|
||||
const stopListening = useCallback(
|
||||
(chainId, address, tokenAddress) => {
|
||||
dispatch({ type: Action.STOP_LISTENING, payload: { chainId, address, tokenAddress } })
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
const update = useCallback((chainId, address, tokenAddress, value, blockNumber) => {
|
||||
dispatch({ type: Action.UPDATE, payload: { chainId, address, tokenAddress, value, blockNumber } })
|
||||
}, [])
|
||||
const update = useCallback(
|
||||
(chainId, address, tokenAddress, value, blockNumber) => {
|
||||
dispatch({ type: Action.UPDATE, payload: { chainId, address, tokenAddress, value, blockNumber } })
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
const batchUpdateAccount = useCallback((chainId, address, tokenAddresses, values, blockNumber) => {
|
||||
dispatch({ type: Action.BATCH_UPDATE_ACCOUNT, payload: { chainId, address, tokenAddresses, values, blockNumber } })
|
||||
}, [])
|
||||
const batchUpdateAccount = useCallback(
|
||||
(chainId, address, tokenAddresses, values, blockNumber) => {
|
||||
dispatch({
|
||||
type: Action.BATCH_UPDATE_ACCOUNT,
|
||||
payload: { chainId, address, tokenAddresses, values, blockNumber }
|
||||
})
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
const batchUpdateExchanges = useCallback((chainId, pairAddresses, tokenAddresses, values, blockNumber) => {
|
||||
dispatch({
|
||||
type: Action.BATCH_UPDATE_EXCHANGES,
|
||||
payload: { chainId, pairAddresses, tokenAddresses, values, blockNumber }
|
||||
})
|
||||
}, [])
|
||||
const batchUpdateExchanges = useCallback(
|
||||
(chainId, pairAddresses, tokenAddresses, values, blockNumber) => {
|
||||
dispatch({
|
||||
type: Action.BATCH_UPDATE_EXCHANGES,
|
||||
payload: { chainId, pairAddresses, tokenAddresses, values, blockNumber }
|
||||
})
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
return (
|
||||
<BalancesContext.Provider
|
||||
|
@ -1,9 +1,7 @@
|
||||
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
|
||||
|
||||
import TxnPopup from '../components/TxnPopup'
|
||||
|
||||
import { useWeb3React } from '../hooks'
|
||||
import { useBlockNumber, usePopups } from './Application'
|
||||
import { useBlockNumber, usePopups } from '../state/application/hooks'
|
||||
|
||||
const ADD = 'ADD'
|
||||
const CHECK = 'CHECK'
|
||||
@ -140,23 +138,17 @@ export function Updater() {
|
||||
finalize(chainId, hash, receipt)
|
||||
// add success or failure popup
|
||||
if (receipt.status === 1) {
|
||||
addPopup(
|
||||
<TxnPopup
|
||||
popKey={1}
|
||||
hash={hash}
|
||||
success={true}
|
||||
summary={allTransactions[hash]?.response?.summary}
|
||||
/>
|
||||
)
|
||||
addPopup({
|
||||
txn: {
|
||||
hash,
|
||||
success: true,
|
||||
summary: allTransactions[hash]?.response?.summary
|
||||
}
|
||||
})
|
||||
} else {
|
||||
addPopup(
|
||||
<TxnPopup
|
||||
popKey={2}
|
||||
hash={hash}
|
||||
success={false}
|
||||
summary={allTransactions[hash]?.response?.summary}
|
||||
/>
|
||||
)
|
||||
addPopup({
|
||||
txn: { hash, success: false, summary: allTransactions[hash]?.response?.summary }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
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'
|
||||
@ -9,8 +10,8 @@ import { NetworkContextName } from '../constants'
|
||||
import { getContract, getExchangeContract, isAddress } from '../utils'
|
||||
|
||||
export function useWeb3React() {
|
||||
const context = useWeb3ReactCore()
|
||||
const contextNetwork = useWeb3ReactCore(NetworkContextName)
|
||||
const context = useWeb3ReactCore<Web3Provider>()
|
||||
const contextNetwork = useWeb3ReactCore<Web3Provider>(NetworkContextName)
|
||||
return context.active ? context : contextNetwork
|
||||
}
|
||||
|
||||
|
@ -3,14 +3,16 @@ import ReactDOM from 'react-dom'
|
||||
import ReactGA from 'react-ga'
|
||||
import { Web3ReactProvider, createWeb3ReactRoot } from '@web3-react/core'
|
||||
import { Web3Provider } from '@ethersproject/providers'
|
||||
import { Provider } from 'react-redux'
|
||||
|
||||
import { NetworkContextName } from './constants'
|
||||
import { isMobile } from 'react-device-detect'
|
||||
import LocalStorageContextProvider, { Updater as LocalStorageContextUpdater } from './contexts/LocalStorage'
|
||||
import ApplicationContextProvider, { Updater as ApplicationContextUpdater } from './contexts/Application'
|
||||
import TransactionContextProvider, { Updater as TransactionContextUpdater } from './contexts/Transactions'
|
||||
import BalancesContextProvider, { Updater as BalancesContextUpdater } from './contexts/Balances'
|
||||
import App from './pages/App'
|
||||
import store from './state'
|
||||
import { Updater as ApplicationContextUpdater } from './state/application/updater'
|
||||
import ThemeProvider, { FixedGlobalStyle, ThemedGlobalStyle } from './theme'
|
||||
import './i18n'
|
||||
|
||||
@ -36,11 +38,9 @@ ReactGA.pageview(window.location.pathname + window.location.search)
|
||||
function ContextProviders({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<LocalStorageContextProvider>
|
||||
<ApplicationContextProvider>
|
||||
<TransactionContextProvider>
|
||||
<BalancesContextProvider>{children}</BalancesContextProvider>
|
||||
</TransactionContextProvider>
|
||||
</ApplicationContextProvider>
|
||||
<TransactionContextProvider>
|
||||
<BalancesContextProvider>{children}</BalancesContextProvider>
|
||||
</TransactionContextProvider>
|
||||
</LocalStorageContextProvider>
|
||||
)
|
||||
}
|
||||
@ -61,15 +61,17 @@ ReactDOM.render(
|
||||
<FixedGlobalStyle />
|
||||
<Web3ReactProvider getLibrary={getLibrary}>
|
||||
<Web3ProviderNetwork getLibrary={getLibrary}>
|
||||
<ContextProviders>
|
||||
<Updaters />
|
||||
<ThemeProvider>
|
||||
<>
|
||||
<ThemedGlobalStyle />
|
||||
<App />
|
||||
</>
|
||||
</ThemeProvider>
|
||||
</ContextProviders>
|
||||
<Provider store={store}>
|
||||
<ContextProviders>
|
||||
<Updaters />
|
||||
<ThemeProvider>
|
||||
<>
|
||||
<ThemedGlobalStyle />
|
||||
<App />
|
||||
</>
|
||||
</ThemeProvider>
|
||||
</ContextProviders>
|
||||
</Provider>
|
||||
</Web3ProviderNetwork>
|
||||
</Web3ReactProvider>
|
||||
</>,
|
||||
|
28
src/state/application/actions.ts
Normal file
28
src/state/application/actions.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { createAction } from '@reduxjs/toolkit'
|
||||
|
||||
export type PopupContent =
|
||||
| {
|
||||
txn: {
|
||||
hash: string
|
||||
success?: boolean
|
||||
summary?: string
|
||||
}
|
||||
}
|
||||
| {
|
||||
poolAdded: {
|
||||
token0?: {
|
||||
address?: string
|
||||
symbol?: string
|
||||
}
|
||||
token1: {
|
||||
address?: string
|
||||
symbol?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const updateBlockNumber = createAction<{ networkId: number; blockNumber: number | null }>('updateBlockNumber')
|
||||
export const toggleWalletModal = createAction<void>('toggleWalletModal')
|
||||
export const toggleUserAdvanced = createAction<void>('toggleUserAdvanced')
|
||||
export const addPopup = createAction<{ content: PopupContent }>('addPopup')
|
||||
export const removePopup = createAction<{ key: string }>('removePopup')
|
49
src/state/application/hooks.ts
Normal file
49
src/state/application/hooks.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { addPopup, PopupContent, removePopup, toggleWalletModal } from './actions'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import { AppState } from '../index'
|
||||
|
||||
export function useBlockNumber() {
|
||||
const { chainId } = useWeb3React()
|
||||
|
||||
return useSelector((state: AppState) => state.application.blockNumber[chainId])
|
||||
}
|
||||
|
||||
export function useWalletModalOpen() {
|
||||
return useSelector((state: AppState) => state.application.walletModalOpen)
|
||||
}
|
||||
|
||||
export function useWalletModalToggle() {
|
||||
const dispatch = useDispatch()
|
||||
return useCallback(() => dispatch(toggleWalletModal()), [dispatch])
|
||||
}
|
||||
|
||||
export function useUserAdvanced() {
|
||||
return useSelector((state: AppState) => state.application.userAdvanced)
|
||||
}
|
||||
|
||||
export function usePopups(): [
|
||||
AppState['application']['popupList'],
|
||||
(content: PopupContent) => void,
|
||||
(key: string) => void
|
||||
] {
|
||||
const dispatch = useDispatch()
|
||||
const activePopups = useSelector((state: AppState) => state.application.popupList.filter(item => item.show))
|
||||
|
||||
const wrappedAddPopup = useCallback(
|
||||
(content: PopupContent) => {
|
||||
dispatch(addPopup({ content }))
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
const wrappedRemovePopup = useCallback(
|
||||
(key: string) => {
|
||||
dispatch(removePopup({ key }))
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
return [activePopups, wrappedAddPopup, wrappedRemovePopup]
|
||||
}
|
53
src/state/application/reducer.ts
Normal file
53
src/state/application/reducer.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { createReducer, nanoid } from '@reduxjs/toolkit'
|
||||
import {
|
||||
addPopup,
|
||||
PopupContent,
|
||||
removePopup,
|
||||
toggleUserAdvanced,
|
||||
toggleWalletModal,
|
||||
updateBlockNumber
|
||||
} from './actions'
|
||||
|
||||
type PopupList = Array<{ key: string; show: boolean; content: PopupContent }>
|
||||
|
||||
interface ApplicationState {
|
||||
blockNumber: { [chainId: number]: number }
|
||||
popupList: PopupList
|
||||
walletModalOpen: boolean
|
||||
userAdvanced: boolean
|
||||
}
|
||||
|
||||
const initialState: ApplicationState = {
|
||||
blockNumber: {},
|
||||
popupList: [],
|
||||
walletModalOpen: false,
|
||||
userAdvanced: false
|
||||
}
|
||||
|
||||
export default createReducer(initialState, builder =>
|
||||
builder
|
||||
.addCase(updateBlockNumber, (state, action) => {
|
||||
const { networkId, blockNumber } = action.payload
|
||||
state.blockNumber[networkId] = blockNumber
|
||||
})
|
||||
.addCase(toggleUserAdvanced, state => {
|
||||
state.userAdvanced = !state.userAdvanced
|
||||
})
|
||||
.addCase(toggleWalletModal, state => {
|
||||
state.walletModalOpen = !state.walletModalOpen
|
||||
})
|
||||
.addCase(addPopup, (state, { payload: { content } }) => {
|
||||
state.popupList.push({
|
||||
key: nanoid(),
|
||||
show: true,
|
||||
content
|
||||
})
|
||||
})
|
||||
.addCase(removePopup, (state, { payload: { key } }) => {
|
||||
state.popupList.forEach(p => {
|
||||
if (p.key === key) {
|
||||
p.show = false
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
41
src/state/application/updater.ts
Normal file
41
src/state/application/updater.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { updateBlockNumber } from './actions'
|
||||
import { useDispatch } from 'react-redux'
|
||||
|
||||
export function Updater() {
|
||||
const { library, chainId } = useWeb3React()
|
||||
const dispatch = useDispatch()
|
||||
|
||||
// update block number
|
||||
useEffect(() => {
|
||||
if (library) {
|
||||
let stale = false
|
||||
|
||||
const update = () => {
|
||||
library
|
||||
.getBlockNumber()
|
||||
.then(blockNumber => {
|
||||
if (!stale) {
|
||||
dispatch(updateBlockNumber({ networkId: chainId, blockNumber }))
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!stale) {
|
||||
dispatch(updateBlockNumber({ networkId: chainId, blockNumber: null }))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
update()
|
||||
library.on('block', update)
|
||||
|
||||
return () => {
|
||||
stale = true
|
||||
library.removeListener('block', update)
|
||||
}
|
||||
}
|
||||
}, [dispatch, chainId, library])
|
||||
|
||||
return null
|
||||
}
|
13
src/state/index.ts
Normal file
13
src/state/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { configureStore } from '@reduxjs/toolkit'
|
||||
import application from './application/reducer'
|
||||
|
||||
const store = configureStore({
|
||||
reducer: {
|
||||
application
|
||||
}
|
||||
})
|
||||
|
||||
export default store
|
||||
|
||||
export type AppState = ReturnType<typeof store.getState>
|
||||
export type AppDispatch = typeof store.dispatch
|
60
yarn.lock
60
yarn.lock
@ -2074,6 +2074,16 @@
|
||||
resolved "https://registry.yarnpkg.com/@reach/visually-hidden/-/visually-hidden-0.1.4.tgz#0dc4ecedf523004337214187db70a46183bd945b"
|
||||
integrity sha512-QHbzXjflSlCvDd6vJwdwx16mSB+vUCCQMiU/wK/CgVNPibtpEiIbisyxkpZc55DyDFNUIqP91rSUsNae+ogGDQ==
|
||||
|
||||
"@reduxjs/toolkit@^1.3.5":
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.3.5.tgz#37c1ab6de9aa66f95bab25a8e9bd9d8ec3b7b80c"
|
||||
integrity sha512-QVqI2T6kwT/3CVdCa6KjUDdEz9YY1eCLQVcRZamiaOAcKI7kkJSh2P0GjaaKXTjIFy0u9sWCXjzifPJNGoXjlw==
|
||||
dependencies:
|
||||
immer "^6.0.1"
|
||||
redux "^4.0.0"
|
||||
redux-thunk "^2.3.0"
|
||||
reselect "^4.0.0"
|
||||
|
||||
"@samverschueren/stream-to-observable@^0.3.0":
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
|
||||
@ -2413,7 +2423,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.5.tgz#527d20ef68571a4af02ed74350164e7a67544860"
|
||||
integrity sha512-wLD/Aq2VggCJXSjxEwrMafIP51Z+13H78nXIX0ABEuIGhmB5sNGbR113MOKo+yfw+RDo1ZU3DM6yfnnRF/+ouw==
|
||||
|
||||
"@types/hoist-non-react-statics@*":
|
||||
"@types/hoist-non-react-statics@*", "@types/hoist-non-react-statics@^3.3.0":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
||||
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
|
||||
@ -2527,6 +2537,16 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-redux@^7.1.8":
|
||||
version "7.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.8.tgz#3631feb559f7858d6ad9eea1d6ef41fa64fe7205"
|
||||
integrity sha512-kpplH7Wg2SYU00sZVT98WBN0ou6QKrYcShRaW+5Vpe5l7bluKWJbWmAL+ieiso07OQzpcP5i1PeY3690640ZWg==
|
||||
dependencies:
|
||||
"@types/hoist-non-react-statics" "^3.3.0"
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
redux "^4.0.0"
|
||||
|
||||
"@types/react-router-dom@^5.0.0":
|
||||
version "5.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.5.tgz#7c334a2ea785dbad2b2dcdd83d2cf3d9973da090"
|
||||
@ -9216,6 +9236,11 @@ immer@1.10.0:
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d"
|
||||
integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==
|
||||
|
||||
immer@^6.0.1:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-6.0.5.tgz#77187d13b71c6cee40dde3b8e87a50a7a636d630"
|
||||
integrity sha512-Q2wd90qrgFieIpLzAO2q9NLEdmyp/sr76Ml4Vm5peUKgyTa2CQa3ey8zuzwSKOlKH7grCeGBGUcLLVCVW1aguA==
|
||||
|
||||
import-cwd@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
|
||||
@ -13993,11 +14018,22 @@ react-i18next@^10.7.0:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
html-parse-stringify2 "2.0.1"
|
||||
|
||||
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4:
|
||||
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.9.0:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
react-redux@^7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d"
|
||||
integrity sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^16.9.0"
|
||||
|
||||
react-remove-scroll-bar@^1.1.5:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-1.2.0.tgz#07250b2bc581f56315759c454c9b159dd04ba49d"
|
||||
@ -14280,6 +14316,19 @@ recursive-readdir@2.2.2:
|
||||
dependencies:
|
||||
minimatch "3.0.4"
|
||||
|
||||
redux-thunk@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
|
||||
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
|
||||
|
||||
redux@^4.0.0:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
|
||||
integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
|
||||
dependencies:
|
||||
loose-envify "^1.4.0"
|
||||
symbol-observable "^1.2.0"
|
||||
|
||||
reflexbox@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/reflexbox/-/reflexbox-4.0.6.tgz#fc756d2cc1ca493baf9b96bb27dd640ad8154cf1"
|
||||
@ -14564,6 +14613,11 @@ requires-port@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
|
||||
|
||||
reselect@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7"
|
||||
integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==
|
||||
|
||||
resolve-cwd@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
|
||||
@ -15938,7 +15992,7 @@ swr@0.1.18:
|
||||
dependencies:
|
||||
fast-deep-equal "2.0.1"
|
||||
|
||||
symbol-observable@^1.1.0:
|
||||
symbol-observable@^1.1.0, symbol-observable@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
|
||||
|
Loading…
Reference in New Issue
Block a user