import React, { useContext, useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { I18nContext } from '../../../contexts/i18n';
import {
getFetchParams,
prepareToLeaveSwaps,
getCurrentSmartTransactions,
getSelectedQuote,
getTopQuote,
getSmartTransactionsOptInStatus,
getSmartTransactionsEnabled,
getCurrentSmartTransactionsEnabled,
getSwapsRefreshStates,
cancelSwapsSmartTransaction,
} from '../../../ducks/swaps/swaps';
import {
isHardwareWallet,
getHardwareWalletType,
} from '../../../selectors/selectors';
import {
DEFAULT_ROUTE,
BUILD_QUOTE_ROUTE,
} from '../../../helpers/constants/routes';
import Typography from '../../../components/ui/typography';
import Box from '../../../components/ui/box';
import UrlIcon from '../../../components/ui/url-icon';
import {
BLOCK_SIZES,
COLORS,
TYPOGRAPHY,
JUSTIFY_CONTENT,
DISPLAY,
FONT_WEIGHT,
ALIGN_ITEMS,
} from '../../../helpers/constants/design-system';
import {
stopPollingForQuotes,
setBackgroundSwapRouteState,
} from '../../../store/actions';
import { SMART_TRANSACTION_STATUSES } from '../../../../shared/constants/transaction';
import SwapsFooter from '../swaps-footer';
import { calcTokenAmount } from '../../../helpers/utils/token-util';
import { showRemainingTimeInMinAndSec } from '../swaps.util';
import { MetaMetricsContext } from '../../../contexts/metametrics.new';
import SuccessIcon from './success-icon';
import RevertedIcon from './reverted-icon';
import CanceledIcon from './canceled-icon';
import UnknownIcon from './unknown-icon';
import ArrowIcon from './arrow-icon';
import TimerIcon from './timer-icon';
export default function SmartTransactionStatus() {
const [cancelSwapLinkClicked, setCancelSwapLinkClicked] = useState(false);
const t = useContext(I18nContext);
const history = useHistory();
const dispatch = useDispatch();
const fetchParams = useSelector(getFetchParams) || {};
const { destinationTokenInfo = {}, sourceTokenInfo = {} } =
fetchParams?.metaData || {};
const hardwareWalletUsed = useSelector(isHardwareWallet);
const hardwareWalletType = useSelector(getHardwareWalletType);
const needsTwoConfirmations = true;
const selectedQuote = useSelector(getSelectedQuote);
const topQuote = useSelector(getTopQuote);
const usedQuote = selectedQuote || topQuote;
const currentSmartTransactions = useSelector(getCurrentSmartTransactions);
const smartTransactionsOptInStatus = useSelector(
getSmartTransactionsOptInStatus,
);
const swapsRefreshRates = useSelector(getSwapsRefreshStates);
const smartTransactionsEnabled = useSelector(getSmartTransactionsEnabled);
const currentSmartTransactionsEnabled = useSelector(
getCurrentSmartTransactionsEnabled,
);
let smartTransactionStatus = SMART_TRANSACTION_STATUSES.PENDING;
let latestSmartTransaction = {};
let latestSmartTransactionUuid;
if (currentSmartTransactions && currentSmartTransactions.length > 0) {
latestSmartTransaction =
currentSmartTransactions[currentSmartTransactions.length - 1];
latestSmartTransactionUuid = latestSmartTransaction?.uuid;
smartTransactionStatus =
latestSmartTransaction?.status || SMART_TRANSACTION_STATUSES.PENDING;
}
const [timeLeftForPendingStxInSec, setTimeLeftForPendingStxInSec] = useState(
swapsRefreshRates.stxStatusDeadline,
);
const sensitiveProperties = {
needs_two_confirmations: needsTwoConfirmations,
token_from: sourceTokenInfo?.symbol,
token_from_amount: fetchParams?.value,
token_to: destinationTokenInfo?.symbol,
request_type: fetchParams?.balanceError ? 'Quote' : 'Order',
slippage: fetchParams?.slippage,
custom_slippage: fetchParams?.slippage === 2,
is_hardware_wallet: hardwareWalletUsed,
hardware_wallet_type: hardwareWalletType,
stx_uuid: latestSmartTransactionUuid,
stx_enabled: smartTransactionsEnabled,
current_stx_enabled: currentSmartTransactionsEnabled,
stx_user_opt_in: smartTransactionsOptInStatus,
};
let destinationValue;
if (usedQuote?.destinationAmount) {
destinationValue = calcTokenAmount(
usedQuote?.destinationAmount,
destinationTokenInfo.decimals,
).toPrecision(8);
}
const trackEvent = useContext(MetaMetricsContext);
const isSmartTransactionPending =
smartTransactionStatus === SMART_TRANSACTION_STATUSES.PENDING;
const showCloseButtonOnly =
isSmartTransactionPending ||
smartTransactionStatus === SMART_TRANSACTION_STATUSES.SUCCESS;
useEffect(() => {
trackEvent({
event: 'STX Status Page Loaded',
category: 'swaps',
sensitiveProperties,
});
// eslint-disable-next-line
}, []);
useEffect(() => {
let intervalId;
if (isSmartTransactionPending && latestSmartTransactionUuid) {
const calculateRemainingTime = () => {
const secondsAfterStxSubmission = Math.round(
(Date.now() - latestSmartTransaction.time) / 1000,
);
if (secondsAfterStxSubmission > swapsRefreshRates.stxStatusDeadline) {
setTimeLeftForPendingStxInSec(0);
clearInterval(intervalId);
return;
}
setTimeLeftForPendingStxInSec(
swapsRefreshRates.stxStatusDeadline - secondsAfterStxSubmission,
);
};
intervalId = setInterval(calculateRemainingTime, 1000);
calculateRemainingTime();
}
return () => clearInterval(intervalId);
}, [
dispatch,
isSmartTransactionPending,
latestSmartTransactionUuid,
latestSmartTransaction.time,
swapsRefreshRates.stxStatusDeadline,
]);
useEffect(() => {
dispatch(setBackgroundSwapRouteState('smartTransactionStatus'));
setTimeout(() => {
// We don't need to poll for quotes on the status page.
dispatch(stopPollingForQuotes());
}, 1000); // Stop polling for quotes after 1s.
}, [dispatch]);
let headerText = t('stxPendingOptimizingGas');
let description;
let subDescription;
let icon;
if (isSmartTransactionPending) {
if (timeLeftForPendingStxInSec < 120) {
headerText = t('stxPendingFinalizing');
} else if (timeLeftForPendingStxInSec < 150) {
headerText = t('stxPendingPrivatelySubmitting');
} else if (cancelSwapLinkClicked) {
headerText = t('stxTryingToCancel');
}
}
if (smartTransactionStatus === SMART_TRANSACTION_STATUSES.SUCCESS) {
headerText = t('stxSuccess');
if (destinationTokenInfo?.symbol) {
description = t('stxSuccessDescription', [destinationTokenInfo.symbol]);
}
icon =