1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-23 02:10:12 +01:00
metamask-extension/ui/pages/swaps/loading-swaps-quotes/loading-swaps-quotes.js
Ariella Vu 78f4684b2a
MetaMetrics: Add EVENT.CATEGORIES const (#14474)
* MetaMetrics: add EVENT.CATEGORIES const

* MetaMetrics: add EVENT.CATEGORIES.INPAGE_PROVIDER

* MetaMetrics: add EVENT.CATEGORIES.AUTH

* MetaMetrics: add EVENT.CATEGORIES.ACCOUNTS pt. 1

* MetaMetrics: add EVENT.CATEGORIES.ACCOUNTS pt. 2
confirm we want to use 'Accounts' instead of 'Account'

* MetaMetrics: add EVENT.CATEGORIES.MESSAGES

* MetaMetrics: add EVENT.CATEGORIES.RETENTION const

* MetaMetrics: add EVENT.CATEGORIES.SETTINGS

* MetaMask: add missing EVENT.CATEGORIES.SNAPS

* MetaMetrics: add EVENT.CATEGORIES.WALLET const

* MetaMetrics: add EVENT.CATEGORIES.ONBOARDING const

* MetaMetrics: add EVENT.CATEGORIES.ONBOARDING
& EVENT.CATEGORIES.TRANSACTIONS consts

* MetaMetrics: use EVENT.CATEGORIES

* ducks/swaps: revert slice name

* MetaMetrics: add missing EVENT.CATEGORIES.NETWORK
2022-04-22 13:09:10 -03:00

181 lines
6.2 KiB
JavaScript

import EventEmitter from 'events';
import React, { useState, useEffect, useRef, useContext } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { shuffle } from 'lodash';
import { useHistory } from 'react-router-dom';
import isEqual from 'lodash/isEqual';
import {
navigateBackToBuildQuote,
getFetchParams,
getQuotesFetchStartTime,
getSmartTransactionsOptInStatus,
getSmartTransactionsEnabled,
getCurrentSmartTransactionsEnabled,
} from '../../../ducks/swaps/swaps';
import {
isHardwareWallet,
getHardwareWalletType,
} from '../../../selectors/selectors';
import { I18nContext } from '../../../contexts/i18n';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import Mascot from '../../../components/ui/mascot';
import { EVENT } from '../../../../shared/constants/metametrics';
import SwapsFooter from '../swaps-footer';
import BackgroundAnimation from './background-animation';
export default function LoadingSwapsQuotes({
aggregatorMetadata,
loadingComplete,
onDone,
}) {
const t = useContext(I18nContext);
const trackEvent = useContext(MetaMetricsContext);
const dispatch = useDispatch();
const history = useHistory();
const animationEventEmitter = useRef(new EventEmitter());
const fetchParams = useSelector(getFetchParams, isEqual);
const quotesFetchStartTime = useSelector(getQuotesFetchStartTime);
const hardwareWalletUsed = useSelector(isHardwareWallet);
const hardwareWalletType = useSelector(getHardwareWalletType);
const smartTransactionsOptInStatus = useSelector(
getSmartTransactionsOptInStatus,
);
const smartTransactionsEnabled = useSelector(getSmartTransactionsEnabled);
const currentSmartTransactionsEnabled = useSelector(
getCurrentSmartTransactionsEnabled,
);
const quotesRequestCancelledEventConfig = {
event: 'Quotes Request Cancelled',
category: EVENT.CATEGORIES.SWAPS,
sensitiveProperties: {
token_from: fetchParams?.sourceTokenInfo?.symbol,
token_from_amount: fetchParams?.value,
request_type: fetchParams?.balanceError,
token_to: fetchParams?.destinationTokenInfo?.symbol,
slippage: fetchParams?.slippage,
custom_slippage: fetchParams?.slippage !== 2,
response_time: Date.now() - quotesFetchStartTime,
is_hardware_wallet: hardwareWalletUsed,
hardware_wallet_type: hardwareWalletType,
stx_enabled: smartTransactionsEnabled,
current_stx_enabled: currentSmartTransactionsEnabled,
stx_user_opt_in: smartTransactionsOptInStatus,
},
};
const [aggregatorNames] = useState(() =>
shuffle(Object.keys(aggregatorMetadata)),
);
const numberOfQuotes = aggregatorNames.length;
const mascotContainer = useRef();
const currentMascotContainer = mascotContainer.current;
const [quoteCount, updateQuoteCount] = useState(0);
const [midPointTarget, setMidpointTarget] = useState(null);
useEffect(() => {
let timeoutLength;
// The below logic simulates a sequential loading of the aggregator quotes, even though we are fetching them all with a single call.
// This is to give the user a sense of progress. The callback passed to `setTimeout` updates the quoteCount and therefore causes
// a new logo to be shown, the fox to look at that logo, the logo bar and aggregator name to update.
if (loadingComplete) {
// If loading is complete, but the quoteCount is not, we quickly display the remaining logos/names/fox looks. 0.2s each
timeoutLength = 20;
} else {
// If loading is not complete, we display remaining logos/names/fox looks at random intervals between 0.5s and 2s, to simulate the
// sort of loading a user would experience in most async scenarios
timeoutLength = 500 + Math.floor(Math.random() * 1500);
}
const quoteCountTimeout = setTimeout(() => {
if (quoteCount < numberOfQuotes) {
updateQuoteCount(quoteCount + 1);
} else if (quoteCount === numberOfQuotes && loadingComplete) {
onDone();
}
}, timeoutLength);
return function cleanup() {
clearTimeout(quoteCountTimeout);
};
}, [quoteCount, loadingComplete, onDone, numberOfQuotes]);
useEffect(() => {
if (currentMascotContainer) {
const {
top,
left,
width,
height,
} = currentMascotContainer.getBoundingClientRect();
const center = { x: left + width / 2, y: top + height / 2 };
setMidpointTarget(center);
}
}, [currentMascotContainer]);
return (
<div className="loading-swaps-quotes">
<div className="loading-swaps-quotes__content">
<>
<div className="loading-swaps-quotes__quote-counter">
<span>
{t('swapFetchingQuoteNofN', [
Math.min(quoteCount + 1, numberOfQuotes),
numberOfQuotes,
])}
</span>
</div>
<div className="loading-swaps-quotes__quote-name-check">
<span>{t('swapFetchingQuotes')}</span>
</div>
<div className="loading-swaps-quotes__loading-bar-container">
<div
className="loading-swaps-quotes__loading-bar"
style={{
width: `${(100 / numberOfQuotes) * quoteCount}%`,
}}
/>
</div>
</>
<div className="loading-swaps-quotes__animation">
<BackgroundAnimation />
<div
className="loading-swaps-quotes__mascot-container"
ref={mascotContainer}
>
<Mascot
animationEventEmitter={animationEventEmitter.current}
width="90"
height="90"
lookAtTarget={midPointTarget}
/>
</div>
</div>
</div>
<SwapsFooter
submitText={t('back')}
onSubmit={async () => {
trackEvent(quotesRequestCancelledEventConfig);
await dispatch(navigateBackToBuildQuote(history));
}}
hideCancel
/>
</div>
);
}
LoadingSwapsQuotes.propTypes = {
loadingComplete: PropTypes.bool.isRequired,
onDone: PropTypes.func.isRequired,
aggregatorMetadata: PropTypes.objectOf(
PropTypes.shape({
title: PropTypes.string,
color: PropTypes.string,
icon: PropTypes.string,
}),
),
};