Commit 31f72a54 authored by Moody Salem's avatar Moody Salem

Clean up transaction modal behavior with new store

parent 287f64cf
......@@ -119,8 +119,8 @@ export default function WalletModal({
confirmedTransactions,
ENSName
}: {
pendingTransactions: any[]
confirmedTransactions: any[]
pendingTransactions: string[] // hashes of pending
confirmedTransactions: string[] // hashes of confirmed
ENSName?: string
}) {
const { active, account, connector, activate, error } = useWeb3React()
......
import React from 'react'
import React, { useMemo } from 'react'
import styled, { css } from 'styled-components'
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 { TransactionDetails } from '../../state/transactions/reducer'
import Identicon from '../Identicon'
import PortisIcon from '../../assets/images/portisIcon.png'
......@@ -121,6 +122,15 @@ const NetworkIcon = styled(Activity)`
height: 16px;
`
// we want the latest one to come first, so return negative if a is after b
function newTranscationsFirst(a: TransactionDetails, b: TransactionDetails) {
return b.addedTime - a.addedTime
}
function recentTransactionsOnly(a: TransactionDetails) {
return new Date().getTime() - a.addedTime < 86_400_000
}
export default function Web3Status() {
const { t } = useTranslation()
const { active, account, connector, error } = useWeb3React()
......@@ -129,8 +139,14 @@ export default function Web3Status() {
const ENSName = useENSName(account)
const allTransactions = useAllTransactions()
const pending = Object.keys(allTransactions).filter(hash => !allTransactions[hash].receipt)
const confirmed = Object.keys(allTransactions).filter(hash => allTransactions[hash].receipt)
const sortedRecentTransactions = useMemo(() => {
const txs = Object.values(allTransactions)
return txs.filter(recentTransactionsOnly).sort(newTranscationsFirst)
}, [allTransactions])
const pending = sortedRecentTransactions.filter(tx => !tx.receipt).map(tx => tx.hash)
const confirmed = sortedRecentTransactions.filter(tx => tx.receipt).map(tx => tx.hash)
const hasPendingTransactions = !!pending.length
......
......@@ -7,12 +7,12 @@ import { Provider } from 'react-redux'
import { NetworkContextName } from './constants'
import { isMobile } from 'react-device-detect'
import { Updater as LocalStorageContextUpdater } from './state/user/hooks'
import { Updater as TransactionContextUpdater } from './state/transactions/hooks'
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 ApplicationUpdater from './state/application/updater'
import TransactionUpdater from './state/transactions/updater'
import UserUpdater from './state/user/updater'
import ThemeProvider, { FixedGlobalStyle, ThemedGlobalStyle } from './theme'
import './i18n'
......@@ -42,9 +42,9 @@ function ContextProviders({ children }: { children: React.ReactNode }) {
function Updaters() {
return (
<>
<LocalStorageContextUpdater />
<ApplicationContextUpdater />
<TransactionContextUpdater />
<UserUpdater />
<ApplicationUpdater />
<TransactionUpdater />
<BalancesContextUpdater />
</>
)
......
......@@ -3,7 +3,7 @@ import { useWeb3React } from '../../hooks'
import { updateBlockNumber } from './actions'
import { useDispatch } from 'react-redux'
export function Updater() {
export default function Updater() {
const { library, chainId } = useWeb3React()
const dispatch = useDispatch()
......
import { TransactionResponse } from '@ethersproject/providers'
import { useCallback, useEffect } from 'react'
import { useCallback } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useWeb3React } from '../../hooks'
import { useAddPopup, useBlockNumber } from '../application/hooks'
import { AppDispatch, AppState } from '../index'
import { addTransaction, checkTransaction, finalizeTransaction } from './actions'
import { addTransaction } from './actions'
import { TransactionDetails } from './reducer'
export function Updater() {
const { chainId, library } = useWeb3React()
const globalBlockNumber = useBlockNumber()
const dispatch = useDispatch<AppDispatch>()
const transactions = useSelector<AppState>(state => state.transactions)
const allTransactions = transactions[chainId] ?? {}
// show popup on confirm
const addPopup = useAddPopup()
useEffect(() => {
if ((chainId || chainId === 0) && library) {
let stale = false
Object.keys(allTransactions)
.filter(
hash => !allTransactions[hash].receipt && allTransactions[hash].blockNumberChecked !== globalBlockNumber
)
.forEach(hash => {
library
.getTransactionReceipt(hash)
.then(receipt => {
if (!stale) {
if (!receipt) {
dispatch(checkTransaction({ chainId, hash, blockNumber: globalBlockNumber }))
} else {
dispatch(
finalizeTransaction({
chainId,
hash,
receipt: {
blockHash: receipt.blockHash,
blockNumber: receipt.blockNumber,
contractAddress: receipt.contractAddress,
from: receipt.from,
status: receipt.status,
to: receipt.to,
transactionHash: receipt.transactionHash,
transactionIndex: receipt.transactionIndex
}
})
)
// add success or failure popup
if (receipt.status === 1) {
addPopup({
txn: {
hash,
success: true,
summary: allTransactions[hash]?.response?.summary
}
})
} else {
addPopup({
txn: { hash, success: false, summary: allTransactions[hash]?.response?.summary }
})
}
}
}
})
.catch(() => {
dispatch(checkTransaction({ chainId, hash, blockNumber: globalBlockNumber }))
})
})
return () => {
stale = true
}
}
}, [chainId, library, allTransactions, globalBlockNumber, dispatch, addPopup])
return null
}
// helper that can take a ethers library transaction response and add it to the list of transactions
export function useTransactionAdder(): (
response: TransactionResponse,
......
import { createReducer } from '@reduxjs/toolkit'
import { addTransaction, checkTransaction, finalizeTransaction, SerializableTransactionReceipt } from './actions'
const now = () => new Date().getTime()
export interface TransactionDetails {
hash: string
approvalOfToken?: string
blockNumberChecked?: number
summary?: string
receipt?: SerializableTransactionReceipt
addedTime: number
confirmedTime?: number
}
export interface TransactionState {
......@@ -23,7 +28,7 @@ export default createReducer(initialState, builder =>
throw Error('Attempted to add existing transaction.')
}
state[chainId] = state[chainId] ?? {}
state[chainId][hash] = { approvalOfToken, summary }
state[chainId][hash] = { hash, approvalOfToken, summary, addedTime: now() }
})
.addCase(checkTransaction, (state, { payload: { chainId, blockNumber, hash } }) => {
if (!state[chainId]?.[hash]) {
......@@ -38,5 +43,6 @@ export default createReducer(initialState, builder =>
}
state[chainId] = state[chainId] ?? {}
state[chainId][hash].receipt = receipt
state[chainId][hash].confirmedTime = now()
})
)
import { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useWeb3React } from '../../hooks'
import { useAddPopup, useBlockNumber } from '../application/hooks'
import { AppDispatch, AppState } from '../index'
import { checkTransaction, finalizeTransaction } from './actions'
export default function Updater() {
const { chainId, library } = useWeb3React()
const globalBlockNumber = useBlockNumber()
const dispatch = useDispatch<AppDispatch>()
const transactions = useSelector<AppState>(state => state.transactions)
const allTransactions = transactions[chainId] ?? {}
// show popup on confirm
const addPopup = useAddPopup()
useEffect(() => {
if ((chainId || chainId === 0) && library) {
let stale = false
Object.keys(allTransactions)
.filter(
hash => !allTransactions[hash].receipt && allTransactions[hash].blockNumberChecked !== globalBlockNumber
)
.forEach(hash => {
library
.getTransactionReceipt(hash)
.then(receipt => {
if (!stale) {
if (!receipt) {
dispatch(checkTransaction({ chainId, hash, blockNumber: globalBlockNumber }))
} else {
dispatch(
finalizeTransaction({
chainId,
hash,
receipt: {
blockHash: receipt.blockHash,
blockNumber: receipt.blockNumber,
contractAddress: receipt.contractAddress,
from: receipt.from,
status: receipt.status,
to: receipt.to,
transactionHash: receipt.transactionHash,
transactionIndex: receipt.transactionIndex
}
})
)
// add success or failure popup
if (receipt.status === 1) {
addPopup({
txn: {
hash,
success: true,
summary: allTransactions[hash]?.summary
}
})
} else {
addPopup({
txn: { hash, success: false, summary: allTransactions[hash]?.summary }
})
}
}
}
})
.catch(() => {
dispatch(checkTransaction({ chainId, hash, blockNumber: globalBlockNumber }))
})
})
return () => {
stale = true
}
}
}, [chainId, library, allTransactions, globalBlockNumber, dispatch, addPopup])
return null
}
import { ChainId, JSBI, Pair, Token, TokenAmount, WETH } from '@uniswap/sdk'
import { useWeb3React } from '@web3-react/core'
import { useCallback, useEffect, useMemo } from 'react'
import { useCallback, useMemo } from 'react'
import { shallowEqual, useDispatch, useSelector } from 'react-redux'
import { useAllTokens } from '../../contexts/Tokens'
import { getTokenDecimals, getTokenName, getTokenSymbol } from '../../utils'
......@@ -12,9 +12,7 @@ import {
removeSerializedToken,
SerializedPair,
SerializedToken,
updateMatchesDarkMode,
updateUserDarkMode,
updateVersion
updateUserDarkMode
} from './actions'
function serializeToken(token: Token): SerializedToken {
......@@ -37,32 +35,6 @@ function deserializeToken(serializedToken: SerializedToken): Token {
)
}
export function Updater() {
const dispatch = useDispatch<AppDispatch>()
useEffect(() => {
dispatch(updateVersion())
}, [dispatch])
// keep dark mode in sync with the system
useEffect(() => {
const darkHandler = (match: MediaQueryListEvent) => {
dispatch(updateMatchesDarkMode({ matchesDarkMode: match.matches }))
}
const match = window?.matchMedia('(prefers-color-scheme: dark)')
dispatch(updateMatchesDarkMode({ matchesDarkMode: match.matches }))
match?.addEventListener('change', darkHandler)
return () => {
match?.removeEventListener('change', darkHandler)
}
}, [dispatch])
return null
}
// this currently isn't used anywhere, but is kept as an example of how to store/update a simple boolean
export function useBetaMessageManager() {
const betaMessageDismissed = useSelector<AppState, boolean>(state => state.user.betaMessageDismissed)
......
import { useEffect } from 'react'
import { useDispatch } from 'react-redux'
import { AppDispatch } from '../index'
import { updateMatchesDarkMode, updateVersion } from './actions'
export default function Updater() {
const dispatch = useDispatch<AppDispatch>()
useEffect(() => {
dispatch(updateVersion())
}, [dispatch])
// keep dark mode in sync with the system
useEffect(() => {
const darkHandler = (match: MediaQueryListEvent) => {
dispatch(updateMatchesDarkMode({ matchesDarkMode: match.matches }))
}
const match = window?.matchMedia('(prefers-color-scheme: dark)')
dispatch(updateMatchesDarkMode({ matchesDarkMode: match.matches }))
match?.addEventListener('change', darkHandler)
return () => {
match?.removeEventListener('change', darkHandler)
}
}, [dispatch])
return null
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment