1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-22 19:26:13 +02:00
metamask-extension/ui/app/hooks/useTransactionDisplayData.js
Brad Decker 706dc02cb4
Implement new transaction list design (#8564)
Co-authored-by: Whymarrh Whitby <whymarrh.whitby@gmail.com>
Co-authored-by: Mark Stacey <markjstacey@gmail.com>
2020-05-26 15:49:11 -05:00

154 lines
6.7 KiB
JavaScript

import { useSelector } from 'react-redux'
import { getKnownMethodData } from '../selectors/selectors'
import { getTransactionActionKey, getStatusKey } from '../helpers/utils/transactions.util'
import { camelCaseToCapitalize } from '../helpers/utils/common.util'
import { useI18nContext } from './useI18nContext'
import { PRIMARY, SECONDARY } from '../helpers/constants/common'
import { getTokenToAddress } from '../helpers/utils/token-util'
import { useUserPreferencedCurrency } from './useUserPreferencedCurrency'
import { formatDateWithYearContext, shortenAddress } from '../helpers/utils/util'
import {
CONTRACT_INTERACTION_KEY,
DEPLOY_CONTRACT_ACTION_KEY,
INCOMING_TRANSACTION,
TOKEN_METHOD_TRANSFER,
TOKEN_METHOD_TRANSFER_FROM,
SEND_ETHER_ACTION_KEY,
TRANSACTION_CATEGORY_APPROVAL,
TRANSACTION_CATEGORY_INTERACTION,
TRANSACTION_CATEGORY_RECEIVE,
TRANSACTION_CATEGORY_SEND,
TRANSACTION_CATEGORY_SIGNATURE_REQUEST,
TOKEN_METHOD_APPROVE,
PENDING_STATUS_HASH,
TOKEN_CATEGORY_HASH,
} from '../helpers/constants/transactions'
import { useCurrencyDisplay } from './useCurrencyDisplay'
import { useTokenDisplayValue } from './useTokenDisplayValue'
import { useTokenData } from './useTokenData'
import { tokenSelector } from '../selectors'
/**
* @typedef {Object} TransactionDisplayData
* @property {string} title - primary description of the transaction
* @property {string} subtitle - supporting text describing the transaction
* @property {string} category - the transaction category
* @property {string} primaryCurrency - the currency string to display in the primary position
* @property {string} [secondaryCurrency] - the currency string to display in the secondary position
* @property {string} status - the status of the transaction
* @property {string} senderAddress - the Ethereum address of the sender
* @property {string} recipientAddress - the Ethereum address of the recipient
*/
/**
* Get computed values used for displaying transaction data to a user
*
* The goal of this method is to perform all of the necessary computation and
* state access required to take a transactionGroup and derive from it a shape
* of data that can power all views related to a transaction. Presently the main
* case is for shared logic between transaction-list-item and transaction-detail-view
* @param {Object} transactionGroup group of transactions
* @return {TransactionDisplayData}
*/
export function useTransactionDisplayData (transactionGroup) {
const knownTokens = useSelector(tokenSelector)
const t = useI18nContext()
const { initialTransaction, primaryTransaction } = transactionGroup
// initialTransaction contains the data we need to derive the primary purpose of this transaction group
const { transactionCategory } = initialTransaction
const { from: senderAddress, to } = initialTransaction.txParams || {}
// for smart contract interactions, methodData can be used to derive the name of the action being taken
const methodData = useSelector((state) => getKnownMethodData(state, initialTransaction?.txParams?.data)) || {}
const actionKey = getTransactionActionKey(initialTransaction)
const status = getStatusKey(primaryTransaction)
const primaryValue = primaryTransaction.txParams?.value
let prefix = '-'
const date = formatDateWithYearContext(initialTransaction.time || 0)
let subtitle
let recipientAddress = to
// This value is used to determine whether we should look inside txParams.data
// to pull out and render token related information
const isTokenCategory = TOKEN_CATEGORY_HASH[transactionCategory]
// these values are always instantiated because they are either
// used by or returned from hooks. Hooks must be called at the top level,
// so as an additional safeguard against inappropriately associating token
// transfers, we pass an additional argument to these hooks that will be
// false for non-token transactions. This additional argument forces the
// hook to return null
const token = isTokenCategory && knownTokens.find((token) => token.address === recipientAddress)
const tokenData = useTokenData(initialTransaction?.txParams?.data, isTokenCategory)
const tokenDisplayValue = useTokenDisplayValue(initialTransaction?.txParams?.data, token, isTokenCategory)
let category
let title
// There are four types of transaction entries that are currently differentiated in the design
// 1. (PENDING DESIGN) signature request
// 2. Send (sendEth sendTokens)
// 3. Deposit
// 4. Site interaction
// 5. Approval
if (transactionCategory == null) {
const origin = initialTransaction.msgParams?.origin || initialTransaction.origin
category = TRANSACTION_CATEGORY_SIGNATURE_REQUEST
title = t('signatureRequest')
subtitle = origin || ''
} else if (transactionCategory === TOKEN_METHOD_APPROVE) {
category = TRANSACTION_CATEGORY_APPROVAL
title = t('approve')
subtitle = initialTransaction.origin
} else if (transactionCategory === DEPLOY_CONTRACT_ACTION_KEY || transactionCategory === CONTRACT_INTERACTION_KEY) {
category = TRANSACTION_CATEGORY_INTERACTION
title = (methodData?.name && camelCaseToCapitalize(methodData.name)) || (actionKey && t(actionKey)) || ''
subtitle = initialTransaction.origin
} else if (transactionCategory === INCOMING_TRANSACTION) {
category = TRANSACTION_CATEGORY_RECEIVE
title = t('receive')
prefix = ''
subtitle = t('fromAddress', [shortenAddress(senderAddress)])
} else if (transactionCategory === TOKEN_METHOD_TRANSFER_FROM || transactionCategory === TOKEN_METHOD_TRANSFER) {
category = TRANSACTION_CATEGORY_SEND
title = t('sendSpecifiedTokens', [token?.symbol || t('token')])
recipientAddress = getTokenToAddress(tokenData.params)
subtitle = t('toAddress', [shortenAddress(recipientAddress)])
} else if (transactionCategory === SEND_ETHER_ACTION_KEY) {
category = TRANSACTION_CATEGORY_SEND
title = t('sendETH')
subtitle = t('toAddress', [shortenAddress(recipientAddress)])
}
const primaryCurrencyPreferences = useUserPreferencedCurrency(PRIMARY)
const secondaryCurrencyPreferences = useUserPreferencedCurrency(SECONDARY)
const [primaryCurrency] = useCurrencyDisplay(primaryValue, {
prefix,
displayValue: isTokenCategory && tokenDisplayValue,
suffix: isTokenCategory && token?.symbol,
...primaryCurrencyPreferences,
})
const [secondaryCurrency] = useCurrencyDisplay(primaryValue, {
prefix,
displayValue: isTokenCategory && tokenDisplayValue,
...secondaryCurrencyPreferences,
})
return {
title,
category,
date,
subtitle,
primaryCurrency,
senderAddress,
recipientAddress,
secondaryCurrency: isTokenCategory ? undefined : secondaryCurrency,
status,
isPending: status in PENDING_STATUS_HASH,
}
}