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 { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useGasFeeInputs } from '../../../hooks/useGasFeeInputs';
|
import { useGasFeeInputs } from '../../../hooks/useGasFeeInputs';
|
||||||
|
import { useShouldAnimateGasEstimations } from '../../../hooks/useShouldAnimateGasEstimations';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
GAS_ESTIMATE_TYPES,
|
GAS_ESTIMATE_TYPES,
|
||||||
@ -27,6 +28,7 @@ import {
|
|||||||
hideSidebar,
|
hideSidebar,
|
||||||
updateTransaction,
|
updateTransaction,
|
||||||
} from '../../../store/actions';
|
} from '../../../store/actions';
|
||||||
|
import LoadingHeartBeat from '../../ui/loading-heartbeat';
|
||||||
|
|
||||||
export default function EditGasPopover({
|
export default function EditGasPopover({
|
||||||
popoverTitle = '',
|
popoverTitle = '',
|
||||||
@ -41,6 +43,8 @@ export default function EditGasPopover({
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const showSidebar = useSelector((state) => state.appState.sidebar.isOpen);
|
const showSidebar = useSelector((state) => state.appState.sidebar.isOpen);
|
||||||
|
|
||||||
|
const shouldAnimate = useShouldAnimateGasEstimations();
|
||||||
|
|
||||||
const showEducationButton =
|
const showEducationButton =
|
||||||
mode === EDIT_GAS_MODES.MODIFY_IN_PLACE && process.env.SHOW_EIP_1559_UI;
|
mode === EDIT_GAS_MODES.MODIFY_IN_PLACE && process.env.SHOW_EIP_1559_UI;
|
||||||
const [showEducationContent, setShowEducationContent] = useState(false);
|
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 ? (
|
{showEducationContent ? (
|
||||||
<EditGasDisplayEducation />
|
<EditGasDisplayEducation />
|
||||||
) : (
|
) : (
|
||||||
<EditGasDisplay
|
<>
|
||||||
showEducationButton={showEducationButton}
|
<LoadingHeartBeat active={shouldAnimate} />
|
||||||
warning={warning}
|
<EditGasDisplay
|
||||||
showAdvancedForm={showAdvancedForm}
|
showEducationButton={showEducationButton}
|
||||||
setShowAdvancedForm={setShowAdvancedForm}
|
warning={warning}
|
||||||
dappSuggestedGasFeeAcknowledged={dappSuggestedGasFeeAcknowledged}
|
showAdvancedForm={showAdvancedForm}
|
||||||
setDappSuggestedGasFeeAcknowledged={
|
setShowAdvancedForm={setShowAdvancedForm}
|
||||||
setDappSuggestedGasFeeAcknowledged
|
dappSuggestedGasFeeAcknowledged={dappSuggestedGasFeeAcknowledged}
|
||||||
}
|
setDappSuggestedGasFeeAcknowledged={
|
||||||
maxPriorityFeePerGas={maxPriorityFeePerGas}
|
setDappSuggestedGasFeeAcknowledged
|
||||||
setMaxPriorityFeePerGas={setMaxPriorityFeePerGas}
|
}
|
||||||
maxPriorityFeePerGasFiat={maxPriorityFeePerGasFiat}
|
maxPriorityFeePerGas={maxPriorityFeePerGas}
|
||||||
maxFeePerGas={maxFeePerGas}
|
setMaxPriorityFeePerGas={setMaxPriorityFeePerGas}
|
||||||
setMaxFeePerGas={setMaxFeePerGas}
|
maxPriorityFeePerGasFiat={maxPriorityFeePerGasFiat}
|
||||||
maxFeePerGasFiat={maxFeePerGasFiat}
|
maxFeePerGas={maxFeePerGas}
|
||||||
estimatedMaximumNative={estimatedMaximumNative}
|
setMaxFeePerGas={setMaxFeePerGas}
|
||||||
isGasEstimatesLoading={isGasEstimatesLoading}
|
maxFeePerGasFiat={maxFeePerGasFiat}
|
||||||
gasFeeEstimates={gasFeeEstimates}
|
estimatedMaximumNative={estimatedMaximumNative}
|
||||||
gasEstimateType={gasEstimateType}
|
isGasEstimatesLoading={isGasEstimatesLoading}
|
||||||
gasPrice={gasPrice}
|
gasFeeEstimates={gasFeeEstimates}
|
||||||
setGasPrice={setGasPrice}
|
gasEstimateType={gasEstimateType}
|
||||||
gasLimit={gasLimit}
|
gasPrice={gasPrice}
|
||||||
setGasLimit={setGasLimit}
|
setGasPrice={setGasPrice}
|
||||||
estimateToUse={estimateToUse}
|
gasLimit={gasLimit}
|
||||||
setEstimateToUse={setEstimateToUse}
|
setGasLimit={setGasLimit}
|
||||||
estimatedMinimumFiat={estimatedMinimumFiat}
|
estimateToUse={estimateToUse}
|
||||||
estimatedMaximumFiat={estimatedMaximumFiat}
|
setEstimateToUse={setEstimateToUse}
|
||||||
hasGasErrors={hasGasErrors}
|
estimatedMinimumFiat={estimatedMinimumFiat}
|
||||||
onEducationClick={() => setShowEducationContent(true)}
|
estimatedMaximumFiat={estimatedMaximumFiat}
|
||||||
mode={mode}
|
onEducationClick={() => setShowEducationContent(true)}
|
||||||
transaction={transaction}
|
mode={mode}
|
||||||
gasErrors={gasErrors}
|
transaction={transaction}
|
||||||
onManualChange={onManualChange}
|
hasGasErrors={hasGasErrors}
|
||||||
{...editGasDisplayProps}
|
gasErrors={gasErrors}
|
||||||
/>
|
onManualChange={onManualChange}
|
||||||
|
{...editGasDisplayProps}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
.transaction-detail {
|
.transaction-detail {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.transaction-detail-edit {
|
.transaction-detail-edit {
|
||||||
text-align: end;
|
text-align: end;
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
import { I18nContext } from '../../../contexts/i18n';
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
|
import { useShouldAnimateGasEstimations } from '../../../hooks/useShouldAnimateGasEstimations';
|
||||||
|
|
||||||
import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component';
|
import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component';
|
||||||
|
import LoadingHeartBeat from '../../ui/loading-heartbeat';
|
||||||
|
|
||||||
export default function TransactionDetail({ rows = [], onEdit }) {
|
export default function TransactionDetail({ rows = [], onEdit }) {
|
||||||
const t = useContext(I18nContext);
|
const t = useContext(I18nContext);
|
||||||
|
const shouldAnimate = useShouldAnimateGasEstimations();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="transaction-detail">
|
||||||
className={classNames('transaction-detail', {
|
<LoadingHeartBeat active={shouldAnimate} />
|
||||||
'transaction-detail--editable': Boolean(onEdit),
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{onEdit && (
|
{onEdit && (
|
||||||
<div className="transaction-detail-edit">
|
<div className="transaction-detail-edit">
|
||||||
<button onClick={onEdit}>{t('edit')}</button>
|
<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 'identicon/index';
|
||||||
@import 'info-tooltip/index';
|
@import 'info-tooltip/index';
|
||||||
@import 'list-item/index';
|
@import 'list-item/index';
|
||||||
|
@import 'loading-heartbeat/index';
|
||||||
@import 'loading-indicator/loading-indicator';
|
@import 'loading-indicator/loading-indicator';
|
||||||
@import 'loading-screen/index';
|
@import 'loading-screen/index';
|
||||||
@import 'menu/menu';
|
@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