import { NativeStackScreenProps } from '@react-navigation/native-stack'
import BigNumber from 'bignumber.js'
import React, { useEffect, useMemo } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { Text } from 'react-native'
import { showError } from 'src/alert/actions'
import AppAnalytics from 'src/analytics/AppAnalytics'
import { SendEvents } from 'src/analytics/Events'
import { ErrorMessages } from 'src/app/ErrorMessages'
import BackButton from 'src/components/BackButton'
import Button, { BtnSizes } from 'src/components/Button'
import {
ReviewContent,
ReviewDetails,
ReviewDetailsItem,
ReviewFooter,
ReviewSummary,
ReviewSummaryItem,
ReviewSummaryItemContact,
ReviewTotalValue,
ReviewTransaction,
} from 'src/components/ReviewTransaction'
import { formatValueToDisplay } from 'src/components/TokenDisplay'
import TokenIcon from 'src/components/TokenIcon'
import { LocalCurrencySymbol } from 'src/localCurrency/consts'
import { getLocalCurrencyCode, getLocalCurrencySymbol } from 'src/localCurrency/selectors'
import { noHeader } from 'src/navigator/Headers'
import { Screens } from 'src/navigator/Screens'
import { StackParamList } from 'src/navigator/types'
import { useDispatch, useSelector } from 'src/redux/hooks'
import { sendPayment } from 'src/send/actions'
import { isSendingSelector } from 'src/send/selectors'
import { usePrepareSendTransactions } from 'src/send/usePrepareSendTransactions'
import { NETWORK_NAMES } from 'src/shared/conts'
import { useAmountAsUsd, useTokenInfo, useTokenToLocalAmount } from 'src/tokens/hooks'
import { feeCurrenciesSelector } from 'src/tokens/selectors'
import Logger from 'src/utils/Logger'
import { getFeeCurrencyAndAmounts } from 'src/viem/prepareTransactions'
import { getSerializablePreparedTransaction } from 'src/viem/preparedTransactionSerialization'
import { walletAddressSelector } from 'src/web3/selectors'
type Props = NativeStackScreenProps<
StackParamList,
Screens.SendConfirmation | Screens.SendConfirmationFromExternal
>
const DEBOUNCE_TIME_MS = 250
const TAG = 'send/SendConfirmation'
export const sendConfirmationScreenNavOptions = noHeader
export default function SendConfirmation(props: Props) {
const { t } = useTranslation()
const dispatch = useDispatch()
const {
origin,
transactionData: { recipient, tokenAmount, tokenAddress, tokenId },
} = props.route.params
const {
prepareTransactionsResult,
refreshPreparedTransactions,
clearPreparedTransactions,
prepareTransactionLoading,
} = usePrepareSendTransactions()
const fromExternal = props.route.name === Screens.SendConfirmationFromExternal
const tokenInfo = useTokenInfo(tokenId)
const isSending = useSelector(isSendingSelector)
const localCurrencyCode = useSelector(getLocalCurrencyCode)
const localCurrencySymbol = useSelector(getLocalCurrencySymbol) ?? LocalCurrencySymbol.USD
const localAmount = useTokenToLocalAmount(tokenAmount, tokenId)
const usdAmount = useAmountAsUsd(tokenAmount, tokenId)
const walletAddress = useSelector(walletAddressSelector)
const feeCurrencies = useSelector((state) => feeCurrenciesSelector(state, tokenInfo!.networkId))
const { maxFeeAmount, feeCurrency: feeTokenInfo } =
getFeeCurrencyAndAmounts(prepareTransactionsResult)
const tokenFeeAmount = maxFeeAmount ?? new BigNumber(0)
const localFeeAmount = useTokenToLocalAmount(tokenFeeAmount, feeTokenInfo?.tokenId)
const networkFeeDisplayAmount = useMemo(
() => ({
token: formatValueToDisplay(tokenFeeAmount),
local: localFeeAmount ? formatValueToDisplay(localFeeAmount) : undefined,
}),
[localFeeAmount, tokenFeeAmount]
)
useEffect(() => {
if (!walletAddress || !tokenInfo) {
return // should never happen
}
clearPreparedTransactions()
const debouncedRefreshTransactions = setTimeout(() => {
return refreshPreparedTransactions({
amount: tokenAmount,
token: tokenInfo,
recipientAddress: recipient.address,
walletAddress,
feeCurrencies,
})
}, DEBOUNCE_TIME_MS)
return () => clearTimeout(debouncedRefreshTransactions)
}, [tokenInfo, tokenAmount, recipient, walletAddress, feeCurrencies])
const disableSend =
isSending || !prepareTransactionsResult || prepareTransactionsResult.type !== 'possible'
const onSend = () => {
const preparedTransaction =
prepareTransactionsResult &&
prepareTransactionsResult.type === 'possible' &&
prepareTransactionsResult.transactions[0]
if (!preparedTransaction) {
// This should never happen because the confirm button is disabled if this happens.
dispatch(showError(ErrorMessages.SEND_PAYMENT_FAILED))
return
}
AppAnalytics.track(SendEvents.send_confirm_send, {
origin,
recipientType: recipient.recipientType,
isScan: props.route.params.isFromScan,
localCurrency: localCurrencyCode,
usdAmount: usdAmount?.toString() ?? null,
localCurrencyAmount: localAmount?.toString() ?? null,
tokenAmount: tokenAmount.toString(),
tokenSymbol: tokenInfo?.symbol ?? '',
tokenAddress: tokenAddress ?? null,
networkId: tokenInfo?.networkId ?? null,
tokenId,
isTokenManuallyImported: !!tokenInfo?.isManuallyImported,
})
dispatch(
sendPayment(
tokenAmount,
tokenId,
usdAmount,
recipient,
fromExternal,
getSerializablePreparedTransaction(preparedTransaction)
)
)
}
// Should never happen
if (!tokenInfo) {
Logger.error(TAG, `tokenInfo is missing`)
return null
}
return (
}
>
}
primaryValue={t('tokenAmount', {
tokenAmount: formatValueToDisplay(tokenAmount),
tokenSymbol: tokenInfo.symbol ?? '',
})}
secondaryValue={t('localAmount', {
localAmount: formatValueToDisplay(localAmount ?? new BigNumber(0)),
localCurrencySymbol,
context: localAmount ? undefined : 'noFiatPrice',
})}
/>
}
/>
}
/>
)
}