Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: display toast message if user quickly sends transaction on different networks #11

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/_locales/en/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions app/scripts/controllers/app-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export default class AppStateController extends EventEmitter {
switchedNetworkDetails: null,
switchedNetworkNeverShowMessage: false,
currentExtensionPopupId: 0,
lastInteractedConfirmationInfo: undefined,
});
this.timer = null;

Expand Down Expand Up @@ -560,6 +561,26 @@ export default class AppStateController extends EventEmitter {
});
}

/**
* The function returns information about the last confirmation user interacted with
*
* @returns {lastInteractedConfirmationInfo}: Information about the last confirmation user interacted with.
*/
getLastInteractedConfirmationInfo() {
return this.store.getState().lastInteractedConfirmationInfo;
}

/**
* Update the information about the last confirmation user interacted with
*
* @param lastInteractedConfirmationInfo
*/
setLastInteractedConfirmationInfo(lastInteractedConfirmationInfo) {
this.store.updateState({
lastInteractedConfirmationInfo,
});
}

/**
* A getter to retrieve currentPopupId saved in the appState
*/
Expand Down
10 changes: 10 additions & 0 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -3683,6 +3683,16 @@ export default class MetamaskController extends EventEmitter {
resolvePendingApproval: this.resolvePendingApproval,
rejectPendingApproval: this.rejectPendingApproval,

getLastInteractedConfirmationInfo:
this.appStateController.getLastInteractedConfirmationInfo.bind(
this.appStateController,
),

setLastInteractedConfirmationInfo:
this.appStateController.setLastInteractedConfirmationInfo.bind(
this.appStateController,
),

// Notifications
resetViewedNotifications: announcementController.resetViewed.bind(
announcementController,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@use "design-system";

.toast_wrapper {
bottom: 70px;
padding-left: 10px;
padding-right: 10px;
padding-bottom: 10px;
position: fixed;
width: 100%;
z-index: design-system.$modal-z-index;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as NetworkChangeToast } from './network-change-toast';
export { default as NetworkChangeToastLegacy } from './network-change-toast-inner';
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react';
import configureStore from 'redux-mock-store';
import {
TransactionStatus,
TransactionType,
} from '@metamask/transaction-controller';

import mockState from '../../../../../../test/data/mock-state.json';
import { renderWithProvider } from '../../../../../../test/lib/render-helpers';

import NetworkChangeToastInner from './network-change-toast-inner';

const render = () => {
const currentConfirmationMock = {
id: '1',
status: TransactionStatus.unapproved,
time: new Date().getTime(),
type: TransactionType.personalSign,
chainId: '0x1',
};

const mockExpectedState = {
...mockState,
metamask: {
...mockState.metamask,
unapprovedPersonalMsgs: {
'1': { ...currentConfirmationMock, msgParams: {} },
},
pendingApprovals: {
'1': {
...currentConfirmationMock,
origin: 'origin',
requestData: {},
requestState: null,
expectsResult: false,
},
},
preferences: { redesignedConfirmationsEnabled: true },
},
confirm: { currentConfirmation: currentConfirmationMock },
};

const defaultStore = configureStore()(mockExpectedState);
return renderWithProvider(
<NetworkChangeToastInner confirmation={currentConfirmationMock} />,
defaultStore,
);
};

describe('NetworkChangeToast', () => {
it('render without throwing error', () => {
expect(() => {
const { container } = render();
expect(container).toBeEmptyDOMElement();
}).not.toThrow();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React, { useCallback, useEffect, useState } from 'react';
import { useSelector } from 'react-redux';

import { Box } from '../../../../../components/component-library';
import { Toast } from '../../../../../components/multichain';
import {
getLastInteractedConfirmationInfo,
setLastInteractedConfirmationInfo,
} from '../../../../../store/actions';
import { getCurrentChainId } from '../../../../../selectors';
import { NETWORK_TO_NAME_MAP } from '../../../../../../shared/constants/network';
import { useI18nContext } from '../../../../../hooks/useI18nContext';

const MILLISECONDS_IN_ONE_MINUTES = 60000;
const MILLISECONDS_IN_FIVE_SECONDS = 5000;

const NetworkChangeToastInner = ({
confirmation,
}: {
confirmation: { id: string };
}) => {
const chainId = useSelector(getCurrentChainId);
const [toastVisible, setToastVisible] = useState(false);
const t = useI18nContext();

const hideToast = useCallback(() => {
setToastVisible(false);
}, [setToastVisible]);

useEffect(() => {
let isMounted = true;
if (!confirmation) {
return undefined;
}
(async () => {
const lastInteractedConfirmationInfo =
await getLastInteractedConfirmationInfo();
const currentTimestamp = new Date().getTime();
if (
lastInteractedConfirmationInfo &&
lastInteractedConfirmationInfo.chainId !== chainId &&
currentTimestamp - lastInteractedConfirmationInfo.timestamp <=
MILLISECONDS_IN_ONE_MINUTES &&
isMounted
) {
setToastVisible(true);
setTimeout(() => {
hideToast();
}, MILLISECONDS_IN_FIVE_SECONDS);
}
if (
(!lastInteractedConfirmationInfo ||
lastInteractedConfirmationInfo?.id !== confirmation.id) &&
isMounted
) {
setLastInteractedConfirmationInfo({
id: confirmation.id,
chainId,
timestamp: new Date().getTime(),
});
}
})();
return () => {
isMounted = false;
};
}, [confirmation?.id]);

if (!toastVisible) {
return null;
}

return (
<Box className="toast_wrapper">
<Toast
onClose={hideToast}
text={t('networkSwitchMessage', [
(NETWORK_TO_NAME_MAP as Record<string, string>)[chainId],
])}
startAdornment={null}
/>
</Box>
);
};

export default NetworkChangeToastInner;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';

import useCurrentConfirmation from '../../../hooks/useCurrentConfirmation';
import NetworkChangeToastInner from './network-change-toast-inner';

// The component has been broken into NetworkChangeToast and NetworkChangeToastInner
// to suffice need of old and re-designed confirmation pages.
// These can be merged once we get rid of old confirmation pages.
const NetworkChangeToast = () => {
const { currentConfirmation } = useCurrentConfirmation();
return <NetworkChangeToastInner confirmation={currentConfirmation} />;
};

export default NetworkChangeToast;
1 change: 1 addition & 0 deletions ui/pages/confirmations/components/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
@import 'confirm/header/header.scss';
@import 'confirm/scroll-to-bottom';
@import 'confirm/nav/nav.scss';
@import "confirm/network-change-toast/index";
@import 'contract-details-modal/index';
@import 'custom-nonce/index';
@import 'edit-gas-display/index';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import SnapLegacyAuthorshipHeader from '../../../../components/app/snaps/snap-le
import InsightWarnings from '../../../../components/app/snaps/insight-warnings';
///: END:ONLY_INCLUDE_IF
import { BlockaidResultType } from '../../../../../shared/constants/security-provider';
import { BlockaidUnavailableBannerAlert } from '../blockaid-unavailable-banner-alert/blockaid-unavailable-banner-alert';
import { NetworkChangeToastLegacy } from '../confirm/network-change-toast';
import SignatureRequestOriginalWarning from './signature-request-original-warning';

export default class SignatureRequestOriginal extends Component {
Expand Down Expand Up @@ -452,6 +452,7 @@ export default class SignatureRequestOriginal extends Component {
{rejectNText}
</ButtonLink>
) : null}
<NetworkChangeToastLegacy confirmation={txData} />
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import SignatureRequestHeader from '../signature-request-header';
import InsightWarnings from '../../../../components/app/snaps/insight-warnings';
///: END:ONLY_INCLUDE_IF
import { BlockaidResultType } from '../../../../../shared/constants/security-provider';
import { NetworkChangeToastLegacy } from '../confirm/network-change-toast';
import Header from './signature-request-siwe-header';
import Message from './signature-request-siwe-message';

Expand Down Expand Up @@ -299,9 +300,7 @@ export default function SignatureRequestSIWE({
}}
/>
)}
{
///: END:ONLY_INCLUDE_IF
}
<NetworkChangeToastLegacy confirmation={txData} />
</>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ import BlockaidBannerAlert from '../security-provider-banner-alert/blockaid-bann

///: BEGIN:ONLY_INCLUDE_IF(snaps)
import InsightWarnings from '../../../../components/app/snaps/insight-warnings';
///: END:ONLY_INCLUDE_IF
import { BlockaidUnavailableBannerAlert } from '../blockaid-unavailable-banner-alert/blockaid-unavailable-banner-alert';
import { NetworkChangeToastLegacy } from '../confirm/network-change-toast';
import Message from './signature-request-message';
import Footer from './signature-request-footer';

Expand Down Expand Up @@ -382,9 +381,7 @@ const SignatureRequest = ({
}}
/>
)}
{
///: END:ONLY_INCLUDE_IF
}
<NetworkChangeToastLegacy confirmation={txData} />
</>
);
};
Expand Down
2 changes: 2 additions & 0 deletions ui/pages/confirmations/confirm-approve/confirm-approve.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { parseStandardTokenTransactionData } from '../../../../shared/modules/tr
import { TokenStandard } from '../../../../shared/constants/transaction';
import { calcTokenAmount } from '../../../../shared/lib/transactions-controller-utils';
import TokenAllowance from '../token-allowance/token-allowance';
import { NetworkChangeToastLegacy } from '../components/confirm/network-change-toast';
import { getCustomTxParamsData } from './confirm-approve.util';
import ConfirmApproveContent from './confirm-approve-content';

Expand Down Expand Up @@ -229,6 +230,7 @@ export default function ConfirmApprove({
</>
)}
</TransactionModalContextProvider>
<NetworkChangeToastLegacy confirmation={transaction} />
</GasFeeContextProvider>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ import { isHardwareKeyring } from '../../../helpers/utils/hardware';
import FeeDetailsComponent from '../components/fee-details-component/fee-details-component';
import { SimulationDetails } from '../components/simulation-details';
import { fetchSwapsFeatureFlags } from '../../swaps/swaps.util';
import { BlockaidUnavailableBannerAlert } from '../components/blockaid-unavailable-banner-alert/blockaid-unavailable-banner-alert';
import { NetworkChangeToastLegacy } from '../components/confirm/network-change-toast';

export default class ConfirmTransactionBase extends Component {
static contextTypes = {
Expand Down Expand Up @@ -1201,6 +1201,7 @@ export default class ConfirmTransactionBase extends Component {
txData={txData}
displayAccountBalanceHeader={displayAccountBalanceHeader}
/>
<NetworkChangeToastLegacy confirmation={txData} />
</TransactionModalContextProvider>
);
}
Expand Down
47 changes: 28 additions & 19 deletions ui/pages/confirmations/confirm/confirm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { MMISignatureMismatchBanner } from '../../../components/app/mmi-signatur
///: END:ONLY_INCLUDE_IF
import { Nav } from '../components/confirm/nav';
import { Title } from '../components/confirm/title';
import { Page } from '../../../components/multichain/pages/page';
import EditGasFeePopover from '../components/edit-gas-fee-popover';
import { NetworkChangeToast } from '../components/confirm/network-change-toast';
import setCurrentConfirmation from '../hooks/setCurrentConfirmation';
import syncConfirmPath from '../hooks/syncConfirmPath';
import { LedgerInfo } from '../components/confirm/ledger-info';
Expand All @@ -25,24 +26,32 @@ const Confirm = () => {
const processAction = useConfirmationAlertActions();

return (
<AlertActionHandlerProvider onProcessAction={processAction}>
<Page className="confirm_wrapper">
<Nav />
<Header />
{
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
<MMISignatureMismatchBanner />
///: END:ONLY_INCLUDE_IF
}
<ScrollToBottom>
<BlockaidLoadingIndicator />
<LedgerInfo />
<Title />
<Info />
</ScrollToBottom>
<Footer />
</Page>
</AlertActionHandlerProvider>
<TransactionModalContextProvider>
{/* This context should be removed once we implement the new edit gas fees popovers */}
<GasFeeContextProvider transaction={currentConfirmation}>
<EIP1559TransactionGasModal />
<ConfirmAlerts>
<Page className="confirm_wrapper">
<Nav />
<Header />
<ScrollToBottom>
{
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
<MMISignatureMismatchBanner />
///: END:ONLY_INCLUDE_IF
}
<BlockaidLoadingIndicator />
<LedgerInfo />
<Title />
<Info />
<PluggableSection />
</ScrollToBottom>
<Footer />
<NetworkChangeToast />
</Page>
</ConfirmAlerts>
</GasFeeContextProvider>
</TransactionModalContextProvider>
);
};

Expand Down
Loading
Loading