import React, { useMemo, useState, useCallback } from 'react'; import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; import { nonceSortedCompletedTransactionsSelector, nonceSortedPendingTransactionsSelector, } from '../../../selectors/transactions'; import { getCurrentChainId } from '../../../selectors'; import { useI18nContext } from '../../../hooks/useI18nContext'; import TransactionListItem from '../transaction-list-item'; import SmartTransactionListItem from '../transaction-list-item/smart-transaction-list-item.component'; import Button from '../../ui/button'; import { TOKEN_CATEGORY_HASH } from '../../../helpers/constants/transactions'; import { SWAPS_CHAINID_CONTRACT_ADDRESS_MAP } from '../../../../shared/constants/swaps'; import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction'; import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; const PAGE_INCREMENT = 10; // When we are on a token page, we only want to show transactions that involve that token. // In the case of token transfers or approvals, these will be transactions sent to the // token contract. In the case of swaps, these will be transactions sent to the swaps contract // and which have the token address in the transaction data. // // getTransactionGroupRecipientAddressFilter is used to determine whether a transaction matches // either of those criteria const getTransactionGroupRecipientAddressFilter = ( recipientAddress, chainId, ) => { return ({ initialTransaction: { txParams } }) => { return ( isEqualCaseInsensitive(txParams?.to, recipientAddress) || (txParams?.to === SWAPS_CHAINID_CONTRACT_ADDRESS_MAP[chainId] && txParams.data.match(recipientAddress.slice(2))) ); }; }; const tokenTransactionFilter = ({ initialTransaction: { type, destinationTokenSymbol, sourceTokenSymbol }, }) => { if (TOKEN_CATEGORY_HASH[type]) { return false; } else if (type === TRANSACTION_TYPES.SWAP) { return destinationTokenSymbol === 'ETH' || sourceTokenSymbol === 'ETH'; } return true; }; const getFilteredTransactionGroups = ( transactionGroups, hideTokenTransactions, tokenAddress, chainId, ) => { if (hideTokenTransactions) { return transactionGroups.filter(tokenTransactionFilter); } else if (tokenAddress) { return transactionGroups.filter( getTransactionGroupRecipientAddressFilter(tokenAddress, chainId), ); } return transactionGroups; }; export default function TransactionList({ hideTokenTransactions, tokenAddress, }) { const [limit, setLimit] = useState(PAGE_INCREMENT); const t = useI18nContext(); const unfilteredPendingTransactions = useSelector( nonceSortedPendingTransactionsSelector, ); const unfilteredCompletedTransactions = useSelector( nonceSortedCompletedTransactionsSelector, ); const chainId = useSelector(getCurrentChainId); const pendingTransactions = useMemo( () => getFilteredTransactionGroups( unfilteredPendingTransactions, hideTokenTransactions, tokenAddress, chainId, ), [ hideTokenTransactions, tokenAddress, unfilteredPendingTransactions, chainId, ], ); const completedTransactions = useMemo( () => getFilteredTransactionGroups( unfilteredCompletedTransactions, hideTokenTransactions, tokenAddress, chainId, ), [ hideTokenTransactions, tokenAddress, unfilteredCompletedTransactions, chainId, ], ); const viewMore = useCallback( () => setLimit((prev) => prev + PAGE_INCREMENT), [], ); return (
{pendingTransactions.length > 0 && (
{`${t('queue')} (${pendingTransactions.length})`}
{pendingTransactions.map((transactionGroup, index) => transactionGroup.initialTransaction.transactionType === TRANSACTION_TYPES.SMART ? ( ) : ( ), )}
)}
{pendingTransactions.length > 0 ? (
{t('history')}
) : null} {completedTransactions.length > 0 ? ( completedTransactions .slice(0, limit) .map((transactionGroup, index) => transactionGroup.initialTransaction?.transactionType === 'smart' ? ( ) : ( ), ) ) : (
{t('noTransactions')}
)} {completedTransactions.length > limit && ( )}
); } TransactionList.propTypes = { hideTokenTransactions: PropTypes.bool, tokenAddress: PropTypes.string, }; TransactionList.defaultProps = { hideTokenTransactions: false, tokenAddress: undefined, };