mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
EIP-1559 - Show gas estimate updating animation in transaction detail (#11566)
This commit is contained in:
parent
6986e76adc
commit
a2be02dfeb
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useGasFeeInputs } from '../../../hooks/useGasFeeInputs';
|
||||
import { useShouldAnimateGasEstimations } from '../../../hooks/useShouldAnimateGasEstimations';
|
||||
|
||||
import {
|
||||
GAS_ESTIMATE_TYPES,
|
||||
@ -27,6 +28,7 @@ import {
|
||||
hideSidebar,
|
||||
updateTransaction,
|
||||
} from '../../../store/actions';
|
||||
import LoadingHeartBeat from '../../ui/loading-heartbeat';
|
||||
|
||||
export default function EditGasPopover({
|
||||
popoverTitle = '',
|
||||
@ -41,6 +43,8 @@ export default function EditGasPopover({
|
||||
const dispatch = useDispatch();
|
||||
const showSidebar = useSelector((state) => state.appState.sidebar.isOpen);
|
||||
|
||||
const shouldAnimate = useShouldAnimateGasEstimations();
|
||||
|
||||
const showEducationButton =
|
||||
mode === EDIT_GAS_MODES.MODIFY_IN_PLACE && process.env.SHOW_EIP_1559_UI;
|
||||
const [showEducationContent, setShowEducationContent] = useState(false);
|
||||
@ -181,45 +185,48 @@ export default function EditGasPopover({
|
||||
)
|
||||
}
|
||||
>
|
||||
<div style={{ padding: '0 20px 20px 20px' }}>
|
||||
<div style={{ padding: '0 20px 20px 20px', position: 'relative' }}>
|
||||
{showEducationContent ? (
|
||||
<EditGasDisplayEducation />
|
||||
) : (
|
||||
<EditGasDisplay
|
||||
showEducationButton={showEducationButton}
|
||||
warning={warning}
|
||||
showAdvancedForm={showAdvancedForm}
|
||||
setShowAdvancedForm={setShowAdvancedForm}
|
||||
dappSuggestedGasFeeAcknowledged={dappSuggestedGasFeeAcknowledged}
|
||||
setDappSuggestedGasFeeAcknowledged={
|
||||
setDappSuggestedGasFeeAcknowledged
|
||||
}
|
||||
maxPriorityFeePerGas={maxPriorityFeePerGas}
|
||||
setMaxPriorityFeePerGas={setMaxPriorityFeePerGas}
|
||||
maxPriorityFeePerGasFiat={maxPriorityFeePerGasFiat}
|
||||
maxFeePerGas={maxFeePerGas}
|
||||
setMaxFeePerGas={setMaxFeePerGas}
|
||||
maxFeePerGasFiat={maxFeePerGasFiat}
|
||||
estimatedMaximumNative={estimatedMaximumNative}
|
||||
isGasEstimatesLoading={isGasEstimatesLoading}
|
||||
gasFeeEstimates={gasFeeEstimates}
|
||||
gasEstimateType={gasEstimateType}
|
||||
gasPrice={gasPrice}
|
||||
setGasPrice={setGasPrice}
|
||||
gasLimit={gasLimit}
|
||||
setGasLimit={setGasLimit}
|
||||
estimateToUse={estimateToUse}
|
||||
setEstimateToUse={setEstimateToUse}
|
||||
estimatedMinimumFiat={estimatedMinimumFiat}
|
||||
estimatedMaximumFiat={estimatedMaximumFiat}
|
||||
hasGasErrors={hasGasErrors}
|
||||
onEducationClick={() => setShowEducationContent(true)}
|
||||
mode={mode}
|
||||
transaction={transaction}
|
||||
gasErrors={gasErrors}
|
||||
onManualChange={onManualChange}
|
||||
{...editGasDisplayProps}
|
||||
/>
|
||||
<>
|
||||
<LoadingHeartBeat active={shouldAnimate} />
|
||||
<EditGasDisplay
|
||||
showEducationButton={showEducationButton}
|
||||
warning={warning}
|
||||
showAdvancedForm={showAdvancedForm}
|
||||
setShowAdvancedForm={setShowAdvancedForm}
|
||||
dappSuggestedGasFeeAcknowledged={dappSuggestedGasFeeAcknowledged}
|
||||
setDappSuggestedGasFeeAcknowledged={
|
||||
setDappSuggestedGasFeeAcknowledged
|
||||
}
|
||||
maxPriorityFeePerGas={maxPriorityFeePerGas}
|
||||
setMaxPriorityFeePerGas={setMaxPriorityFeePerGas}
|
||||
maxPriorityFeePerGasFiat={maxPriorityFeePerGasFiat}
|
||||
maxFeePerGas={maxFeePerGas}
|
||||
setMaxFeePerGas={setMaxFeePerGas}
|
||||
maxFeePerGasFiat={maxFeePerGasFiat}
|
||||
estimatedMaximumNative={estimatedMaximumNative}
|
||||
isGasEstimatesLoading={isGasEstimatesLoading}
|
||||
gasFeeEstimates={gasFeeEstimates}
|
||||
gasEstimateType={gasEstimateType}
|
||||
gasPrice={gasPrice}
|
||||
setGasPrice={setGasPrice}
|
||||
gasLimit={gasLimit}
|
||||
setGasLimit={setGasLimit}
|
||||
estimateToUse={estimateToUse}
|
||||
setEstimateToUse={setEstimateToUse}
|
||||
estimatedMinimumFiat={estimatedMinimumFiat}
|
||||
estimatedMaximumFiat={estimatedMaximumFiat}
|
||||
onEducationClick={() => setShowEducationContent(true)}
|
||||
mode={mode}
|
||||
transaction={transaction}
|
||||
hasGasErrors={hasGasErrors}
|
||||
gasErrors={gasErrors}
|
||||
onManualChange={onManualChange}
|
||||
{...editGasDisplayProps}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Popover>
|
||||
|
@ -1,4 +1,6 @@
|
||||
.transaction-detail {
|
||||
position: relative;
|
||||
|
||||
.transaction-detail-edit {
|
||||
text-align: end;
|
||||
padding-top: 20px;
|
||||
|
@ -1,20 +1,19 @@
|
||||
import React, { useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { I18nContext } from '../../../contexts/i18n';
|
||||
import { useShouldAnimateGasEstimations } from '../../../hooks/useShouldAnimateGasEstimations';
|
||||
|
||||
import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component';
|
||||
import LoadingHeartBeat from '../../ui/loading-heartbeat';
|
||||
|
||||
export default function TransactionDetail({ rows = [], onEdit }) {
|
||||
const t = useContext(I18nContext);
|
||||
const shouldAnimate = useShouldAnimateGasEstimations();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('transaction-detail', {
|
||||
'transaction-detail--editable': Boolean(onEdit),
|
||||
})}
|
||||
>
|
||||
<div className="transaction-detail">
|
||||
<LoadingHeartBeat active={shouldAnimate} />
|
||||
{onEdit && (
|
||||
<div className="transaction-detail-edit">
|
||||
<button onClick={onEdit}>{t('edit')}</button>
|
||||
|
36
ui/components/ui/loading-heartbeat/index.js
Normal file
36
ui/components/ui/loading-heartbeat/index.js
Normal file
@ -0,0 +1,36 @@
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
|
||||
export default function LoadingHeartBeat({ active }) {
|
||||
const heartNode = useRef(null);
|
||||
|
||||
const LOADING_CLASS = 'loading-heartbeat--active';
|
||||
|
||||
// When the loading animation completes, remove the className to disappear again
|
||||
useEffect(() => {
|
||||
const eventName = 'animationend';
|
||||
const node = heartNode?.current;
|
||||
const eventHandler = () => {
|
||||
node?.classList.remove(LOADING_CLASS);
|
||||
};
|
||||
|
||||
node?.addEventListener(eventName, eventHandler);
|
||||
return () => {
|
||||
node?.removeEventListener(eventName, eventHandler);
|
||||
};
|
||||
}, [heartNode]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('loading-heartbeat', {
|
||||
[LOADING_CLASS]: active,
|
||||
})}
|
||||
ref={heartNode}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
|
||||
LoadingHeartBeat.propTypes = {
|
||||
active: PropTypes.bool,
|
||||
};
|
23
ui/components/ui/loading-heartbeat/index.scss
Normal file
23
ui/components/ui/loading-heartbeat/index.scss
Normal file
@ -0,0 +1,23 @@
|
||||
.loading-heartbeat {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
background: #fff;
|
||||
display: none;
|
||||
|
||||
&--active {
|
||||
display: block;
|
||||
animation: heartbeat 2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heartbeat {
|
||||
0% { opacity: 0; }
|
||||
25% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
75% { opacity: 1; }
|
||||
100% { opacity: 0; }
|
||||
}
|
@ -31,6 +31,7 @@
|
||||
@import 'identicon/index';
|
||||
@import 'info-tooltip/index';
|
||||
@import 'list-item/index';
|
||||
@import 'loading-heartbeat/index';
|
||||
@import 'loading-indicator/loading-indicator';
|
||||
@import 'loading-screen/index';
|
||||
@import 'menu/menu';
|
||||
|
28
ui/hooks/useShouldAnimateGasEstimations.js
Normal file
28
ui/hooks/useShouldAnimateGasEstimations.js
Normal file
@ -0,0 +1,28 @@
|
||||
import { useRef } from 'react';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import { useGasFeeEstimates } from './useGasFeeEstimates';
|
||||
|
||||
export function useShouldAnimateGasEstimations() {
|
||||
const { isGasEstimatesLoading, gasFeeEstimates } = useGasFeeEstimates();
|
||||
|
||||
// Do the animation only when gas prices have changed...
|
||||
const lastGasEstimates = useRef(gasFeeEstimates);
|
||||
const gasEstimatesChanged = !isEqual(
|
||||
lastGasEstimates.current,
|
||||
gasFeeEstimates,
|
||||
);
|
||||
|
||||
// ... and only if gas didn't just load
|
||||
// Removing this line will cause the initial loading screen to stay empty
|
||||
const gasJustLoaded = isEqual(lastGasEstimates.current, {});
|
||||
|
||||
if (gasEstimatesChanged) {
|
||||
lastGasEstimates.current = gasFeeEstimates;
|
||||
}
|
||||
|
||||
const showLoadingAnimation =
|
||||
isGasEstimatesLoading || (gasEstimatesChanged && !gasJustLoaded);
|
||||
|
||||
return showLoadingAnimation;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user