mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Added the ability to navigate multiple SIWE notifications (#18103)
This commit is contained in:
parent
2e856894cc
commit
f3147bcfb7
@ -5,6 +5,76 @@ exports[`SignatureRequestSIWE (Sign in with Ethereum) should match snapshot 1`]
|
|||||||
<div
|
<div
|
||||||
class="signature-request-siwe"
|
class="signature-request-siwe"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
class="request-signature__navigation"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="confirm-page-container-navigation"
|
||||||
|
style="display: none;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="confirm-page-container-navigation__container"
|
||||||
|
data-testid="navigation-container"
|
||||||
|
style="visibility: hidden;"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="confirm-page-container-navigation__arrow"
|
||||||
|
data-testid="first-page"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-angle-double-left fa-2x"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="confirm-page-container-navigation__arrow"
|
||||||
|
data-testid="previous-page"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-angle-left fa-2x"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="confirm-page-container-navigation__textcontainer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="confirm-page-container-navigation__navtext"
|
||||||
|
>
|
||||||
|
0
|
||||||
|
|
||||||
|
of
|
||||||
|
|
||||||
|
1
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="confirm-page-container-navigation__longtext"
|
||||||
|
>
|
||||||
|
requests waiting to be acknowledged
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="confirm-page-container-navigation__container"
|
||||||
|
style="visibility: initial;"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="confirm-page-container-navigation__arrow"
|
||||||
|
data-testid="next-page"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-angle-right fa-2x"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="confirm-page-container-navigation__arrow"
|
||||||
|
data-testid="last-page"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-angle-double-right fa-2x"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
class="box network-account-balance-header box--padding-4 box--display-flex box--flex-direction-row box--justify-content-space-between box--align-items-center"
|
class="box network-account-balance-header box--padding-4 box--display-flex box--flex-direction-row box--justify-content-space-between box--align-items-center"
|
||||||
>
|
>
|
||||||
|
@ -1,20 +1,25 @@
|
|||||||
import React, { useCallback, useContext, useState } from 'react';
|
import React, { useCallback, useContext, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
import log from 'loglevel';
|
import log from 'loglevel';
|
||||||
import { isValidSIWEOrigin } from '@metamask/controller-utils';
|
import { isValidSIWEOrigin } from '@metamask/controller-utils';
|
||||||
import { BannerAlert, Text } from '../../component-library';
|
import { BannerAlert, Text } from '../../component-library';
|
||||||
import Popover from '../../ui/popover';
|
import Popover from '../../ui/popover';
|
||||||
import Checkbox from '../../ui/check-box';
|
import Checkbox from '../../ui/check-box';
|
||||||
|
import Button from '../../ui/button';
|
||||||
import { I18nContext } from '../../../contexts/i18n';
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
import { PageContainerFooter } from '../../ui/page-container';
|
import { PageContainerFooter } from '../../ui/page-container';
|
||||||
import { isAddressLedger } from '../../../ducks/metamask/metamask';
|
import { isAddressLedger } from '../../../ducks/metamask/metamask';
|
||||||
import {
|
import {
|
||||||
accountsWithSendEtherInfoSelector,
|
accountsWithSendEtherInfoSelector,
|
||||||
getSubjectMetadata,
|
getSubjectMetadata,
|
||||||
|
getTotalUnapprovedMessagesCount,
|
||||||
|
unconfirmedMessagesHashSelector,
|
||||||
} from '../../../selectors';
|
} from '../../../selectors';
|
||||||
import { getAccountByAddress } from '../../../helpers/utils/util';
|
import { getAccountByAddress, valuesFor } from '../../../helpers/utils/util';
|
||||||
import { formatMessageParams } from '../../../../shared/modules/siwe';
|
import { formatMessageParams } from '../../../../shared/modules/siwe';
|
||||||
|
import { clearConfirmTransaction } from '../../../ducks/confirm-transaction/confirm-transaction.duck';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SEVERITIES,
|
SEVERITIES,
|
||||||
@ -23,6 +28,9 @@ import {
|
|||||||
|
|
||||||
import SecurityProviderBannerMessage from '../security-provider-banner-message/security-provider-banner-message';
|
import SecurityProviderBannerMessage from '../security-provider-banner-message/security-provider-banner-message';
|
||||||
import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../security-provider-banner-message/security-provider-banner-message.constants';
|
import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../security-provider-banner-message/security-provider-banner-message.constants';
|
||||||
|
import ConfirmPageContainerNavigation from '../confirm-page-container/confirm-page-container-navigation';
|
||||||
|
import { getMostRecentOverviewPage } from '../../../ducks/history/history';
|
||||||
|
import { showModal, cancelMsgs } from '../../../store/actions';
|
||||||
import LedgerInstructionField from '../ledger-instruction-field';
|
import LedgerInstructionField from '../ledger-instruction-field';
|
||||||
|
|
||||||
import SignatureRequestHeader from '../signature-request-header';
|
import SignatureRequestHeader from '../signature-request-header';
|
||||||
@ -34,11 +42,17 @@ export default function SignatureRequestSIWE({
|
|||||||
cancelPersonalMessage,
|
cancelPersonalMessage,
|
||||||
signPersonalMessage,
|
signPersonalMessage,
|
||||||
}) {
|
}) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const history = useHistory();
|
||||||
const t = useContext(I18nContext);
|
const t = useContext(I18nContext);
|
||||||
|
|
||||||
const allAccounts = useSelector(accountsWithSendEtherInfoSelector);
|
const allAccounts = useSelector(accountsWithSendEtherInfoSelector);
|
||||||
const subjectMetadata = useSelector(getSubjectMetadata);
|
const subjectMetadata = useSelector(getSubjectMetadata);
|
||||||
|
|
||||||
|
const messagesCount = useSelector(getTotalUnapprovedMessagesCount);
|
||||||
|
const messagesList = useSelector(unconfirmedMessagesHashSelector);
|
||||||
|
const mostRecentOverviewPage = useSelector(getMostRecentOverviewPage);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
msgParams: {
|
msgParams: {
|
||||||
from,
|
from,
|
||||||
@ -90,8 +104,29 @@ export default function SignatureRequestSIWE({
|
|||||||
[cancelPersonalMessage],
|
[cancelPersonalMessage],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCancelAll = () => {
|
||||||
|
const unapprovedTxCount = messagesCount;
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
showModal({
|
||||||
|
name: 'REJECT_TRANSACTIONS',
|
||||||
|
unapprovedTxCount,
|
||||||
|
onSubmit: async () => {
|
||||||
|
await dispatch(cancelMsgs(valuesFor(messagesList)));
|
||||||
|
dispatch(clearConfirmTransaction());
|
||||||
|
history.push(mostRecentOverviewPage);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const rejectNText = t('rejectRequestsN', [messagesCount]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="signature-request-siwe">
|
<div className="signature-request-siwe">
|
||||||
|
<div className="request-signature__navigation">
|
||||||
|
<ConfirmPageContainerNavigation />
|
||||||
|
</div>
|
||||||
<SignatureRequestHeader txData={txData} />
|
<SignatureRequestHeader txData={txData} />
|
||||||
<Header
|
<Header
|
||||||
fromAccount={fromAccount}
|
fromAccount={fromAccount}
|
||||||
@ -150,6 +185,18 @@ export default function SignatureRequestSIWE({
|
|||||||
submitText={t('signin')}
|
submitText={t('signin')}
|
||||||
submitButtonType={isSIWEDomainValid ? 'primary' : 'danger-primary'}
|
submitButtonType={isSIWEDomainValid ? 'primary' : 'danger-primary'}
|
||||||
/>
|
/>
|
||||||
|
{messagesCount > 1 ? (
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
className="request-signature__container__reject"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
handleCancelAll();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{rejectNText}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
{isShowingDomainWarning && (
|
{isShowingDomainWarning && (
|
||||||
<Popover
|
<Popover
|
||||||
onClose={() => setIsShowingDomainWarning(false)}
|
onClose={() => setIsShowingDomainWarning(false)}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
|
import { fireEvent } from '@testing-library/react';
|
||||||
import { MESSAGE_TYPE } from '../../../../shared/constants/app';
|
import { MESSAGE_TYPE } from '../../../../shared/constants/app';
|
||||||
import mockState from '../../../../test/data/mock-state.json';
|
import mockState from '../../../../test/data/mock-state.json';
|
||||||
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||||
@ -21,6 +22,14 @@ const mockStoreInitialState = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockShowModal = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('../../../store/actions.ts', () => {
|
||||||
|
return {
|
||||||
|
showModal: () => mockShowModal,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const mockProps = {
|
const mockProps = {
|
||||||
cancelPersonalMessage: jest.fn(),
|
cancelPersonalMessage: jest.fn(),
|
||||||
signPersonalMessage: jest.fn(),
|
signPersonalMessage: jest.fn(),
|
||||||
@ -136,4 +145,84 @@ describe('SignatureRequestSIWE (Sign in with Ethereum)', () => {
|
|||||||
container.querySelector('.mock-ledger-instruction-field'),
|
container.querySelector('.mock-ledger-instruction-field'),
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when there is only one unconfirmed tx', () => {
|
||||||
|
it('should not show multiple notifications header', () => {
|
||||||
|
const store = configureStore(mockStoreInitialState);
|
||||||
|
const txData = cloneDeep(mockProps.txData);
|
||||||
|
|
||||||
|
const { container } = renderWithProvider(
|
||||||
|
<SignatureRequestSIWE {...mockProps} txData={txData} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
container.querySelector('.confirm-page-container-navigation'),
|
||||||
|
).toHaveStyle('display: none');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not show Reject request button', () => {
|
||||||
|
const { container } = render();
|
||||||
|
expect(
|
||||||
|
container.querySelector('.request-signature__container__reject'),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when there is more than one unconfirmed tx', () => {
|
||||||
|
let renderResult;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const store = configureStore({
|
||||||
|
...mockStoreInitialState,
|
||||||
|
metamask: {
|
||||||
|
...mockStoreInitialState.metamask,
|
||||||
|
unapprovedTxs: {
|
||||||
|
...mockStoreInitialState.metamask.unapprovedTxs,
|
||||||
|
'0x12333': {
|
||||||
|
chainId: mockStoreInitialState.metamask.providerConfig.chainId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unapprovedMsgCount: 2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const txData = cloneDeep(mockProps.txData);
|
||||||
|
renderResult = renderWithProvider(
|
||||||
|
<SignatureRequestSIWE {...mockProps} txData={txData} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
renderResult = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show multiple notifications header', () => {
|
||||||
|
const { container } = renderResult;
|
||||||
|
expect(
|
||||||
|
container.getElementsByClassName('signature-request-siwe-header'),
|
||||||
|
).toHaveLength(1);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
container.querySelector('.confirm-page-container-navigation'),
|
||||||
|
).toHaveStyle('display: flex');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show Reject request button', () => {
|
||||||
|
const { getByText } = renderResult;
|
||||||
|
const cancelAll = getByText('Reject 2 requests');
|
||||||
|
|
||||||
|
expect(cancelAll).toHaveClass('request-signature__container__reject');
|
||||||
|
expect(cancelAll).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show cancel all modal on Reject request button click', () => {
|
||||||
|
const { getByText } = renderResult;
|
||||||
|
const cancelAll = getByText('Reject 2 requests');
|
||||||
|
|
||||||
|
fireEvent.click(cancelAll);
|
||||||
|
expect(mockShowModal).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -61,7 +61,7 @@ const mockStoreWithEth = {
|
|||||||
cachedBalances: {},
|
cachedBalances: {},
|
||||||
unapprovedDecryptMsgs: {},
|
unapprovedDecryptMsgs: {},
|
||||||
unapprovedEncryptionPublicKeyMsgs: {},
|
unapprovedEncryptionPublicKeyMsgs: {},
|
||||||
uncofirmedTransactions: {},
|
unconfirmedTransactions: {},
|
||||||
selectedAddress: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
selectedAddress: '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5',
|
||||||
nativeCurrency: 'ETH',
|
nativeCurrency: 'ETH',
|
||||||
currentCurrency: 'usd',
|
currentCurrency: 'usd',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user