mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
parent
c2cd6f8097
commit
a0c4febfce
@ -20,6 +20,10 @@
|
||||
@media screen and (min-width: $break-large) {
|
||||
height: 620px;
|
||||
}
|
||||
|
||||
&__reject {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__typed-container {
|
||||
|
@ -38,6 +38,9 @@ export default class SignatureRequestOriginal extends Component {
|
||||
hardwareWalletRequiresConnection: PropTypes.bool,
|
||||
isLedgerWallet: PropTypes.bool,
|
||||
nativeCurrency: PropTypes.string.isRequired,
|
||||
messagesCount: PropTypes.number,
|
||||
showRejectTransactionsConfirmationModal: PropTypes.func.isRequired,
|
||||
cancelAll: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
@ -315,7 +318,31 @@ export default class SignatureRequestOriginal extends Component {
|
||||
);
|
||||
};
|
||||
|
||||
handleCancelAll = () => {
|
||||
const {
|
||||
cancelAll,
|
||||
clearConfirmTransaction,
|
||||
history,
|
||||
mostRecentOverviewPage,
|
||||
showRejectTransactionsConfirmationModal,
|
||||
messagesCount,
|
||||
} = this.props;
|
||||
const unapprovedTxCount = messagesCount;
|
||||
|
||||
showRejectTransactionsConfirmationModal({
|
||||
unapprovedTxCount,
|
||||
onSubmit: async () => {
|
||||
await cancelAll();
|
||||
clearConfirmTransaction();
|
||||
history.push(mostRecentOverviewPage);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
render = () => {
|
||||
const { messagesCount } = this.props;
|
||||
const { t } = this.context;
|
||||
const rejectNText = t('rejectTxsN', [messagesCount]);
|
||||
return (
|
||||
<div className="request-signature__container">
|
||||
{this.renderHeader()}
|
||||
@ -326,6 +353,15 @@ export default class SignatureRequestOriginal extends Component {
|
||||
</div>
|
||||
) : null}
|
||||
{this.renderFooter()}
|
||||
{messagesCount > 1 ? (
|
||||
<Button
|
||||
type="link"
|
||||
className="request-signature__container__reject"
|
||||
onClick={() => this.handleCancelAll()}
|
||||
>
|
||||
{rejectNText}
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -3,14 +3,16 @@ import { compose } from 'redux';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import { MESSAGE_TYPE } from '../../../../shared/constants/app';
|
||||
import { goHome } from '../../../store/actions';
|
||||
import { goHome, cancelMsgs, showModal } from '../../../store/actions';
|
||||
import {
|
||||
accountsWithSendEtherInfoSelector,
|
||||
conversionRateSelector,
|
||||
getSubjectMetadata,
|
||||
doesAddressRequireLedgerHidConnection,
|
||||
unconfirmedMessagesHashSelector,
|
||||
getTotalUnapprovedMessagesCount,
|
||||
} from '../../../selectors';
|
||||
import { getAccountByAddress } from '../../../helpers/utils/util';
|
||||
import { getAccountByAddress, valuesFor } from '../../../helpers/utils/util';
|
||||
import { clearConfirmTransaction } from '../../../ducks/confirm-transaction/confirm-transaction.duck';
|
||||
import { getMostRecentOverviewPage } from '../../../ducks/history/history';
|
||||
import {
|
||||
@ -29,6 +31,8 @@ function mapStateToProps(state, ownProps) {
|
||||
from,
|
||||
);
|
||||
const isLedgerWallet = isAddressLedger(state, from);
|
||||
const messagesList = unconfirmedMessagesHashSelector(state);
|
||||
const messagesCount = getTotalUnapprovedMessagesCount(state);
|
||||
|
||||
return {
|
||||
requester: null,
|
||||
@ -41,6 +45,8 @@ function mapStateToProps(state, ownProps) {
|
||||
// not passed to component
|
||||
allAccounts: accountsWithSendEtherInfoSelector(state),
|
||||
subjectMetadata: getSubjectMetadata(state),
|
||||
messagesList,
|
||||
messagesCount,
|
||||
};
|
||||
}
|
||||
|
||||
@ -48,6 +54,19 @@ function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
goHome: () => dispatch(goHome()),
|
||||
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
|
||||
showRejectTransactionsConfirmationModal: ({
|
||||
onSubmit,
|
||||
unapprovedTxCount: messagesCount,
|
||||
}) => {
|
||||
return dispatch(
|
||||
showModal({
|
||||
name: 'REJECT_TRANSACTIONS',
|
||||
onSubmit,
|
||||
unapprovedTxCount: messagesCount,
|
||||
}),
|
||||
);
|
||||
},
|
||||
cancelAll: (messagesList) => dispatch(cancelMsgs(messagesList)),
|
||||
};
|
||||
}
|
||||
|
||||
@ -62,7 +81,7 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
|
||||
txData,
|
||||
} = ownProps;
|
||||
|
||||
const { allAccounts, ...otherStateProps } = stateProps;
|
||||
const { allAccounts, messagesList, ...otherStateProps } = stateProps;
|
||||
|
||||
const {
|
||||
type,
|
||||
@ -71,6 +90,8 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
|
||||
|
||||
const fromAccount = getAccountByAddress(allAccounts, from);
|
||||
|
||||
const { cancelAll: dispatchCancelAll } = dispatchProps;
|
||||
|
||||
let cancel;
|
||||
let sign;
|
||||
if (type === MESSAGE_TYPE.PERSONAL_SIGN) {
|
||||
@ -92,6 +113,7 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
|
||||
txData,
|
||||
cancel,
|
||||
sign,
|
||||
cancelAll: () => dispatchCancelAll(valuesFor(messagesList)),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -117,6 +117,29 @@ export const unconfirmedTransactionsHashSelector = createSelector(
|
||||
},
|
||||
);
|
||||
|
||||
export const unconfirmedMessagesHashSelector = createSelector(
|
||||
unapprovedMsgsSelector,
|
||||
unapprovedPersonalMsgsSelector,
|
||||
unapprovedDecryptMsgsSelector,
|
||||
unapprovedEncryptionPublicKeyMsgsSelector,
|
||||
unapprovedTypedMessagesSelector,
|
||||
(
|
||||
unapprovedMsgs = {},
|
||||
unapprovedPersonalMsgs = {},
|
||||
unapprovedDecryptMsgs = {},
|
||||
unapprovedEncryptionPublicKeyMsgs = {},
|
||||
unapprovedTypedMessages = {},
|
||||
) => {
|
||||
return {
|
||||
...unapprovedMsgs,
|
||||
...unapprovedPersonalMsgs,
|
||||
...unapprovedDecryptMsgs,
|
||||
...unapprovedEncryptionPublicKeyMsgs,
|
||||
...unapprovedTypedMessages,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const unapprovedMsgCountSelector = (state) => state.metamask.unapprovedMsgCount;
|
||||
const unapprovedPersonalMsgCountSelector = (state) =>
|
||||
state.metamask.unapprovedPersonalMsgCount;
|
||||
|
@ -470,6 +470,24 @@ export function getTotalUnapprovedCount(state) {
|
||||
);
|
||||
}
|
||||
|
||||
export function getTotalUnapprovedMessagesCount(state) {
|
||||
const {
|
||||
unapprovedMsgCount = 0,
|
||||
unapprovedPersonalMsgCount = 0,
|
||||
unapprovedDecryptMsgCount = 0,
|
||||
unapprovedEncryptionPublicKeyMsgCount = 0,
|
||||
unapprovedTypedMessagesCount = 0,
|
||||
} = state.metamask;
|
||||
|
||||
return (
|
||||
unapprovedMsgCount +
|
||||
unapprovedPersonalMsgCount +
|
||||
unapprovedDecryptMsgCount +
|
||||
unapprovedEncryptionPublicKeyMsgCount +
|
||||
unapprovedTypedMessagesCount
|
||||
);
|
||||
}
|
||||
|
||||
function getUnapprovedTxCount(state) {
|
||||
const { unapprovedTxs = {} } = state.metamask;
|
||||
return Object.keys(unapprovedTxs).length;
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||
ORIGIN_METAMASK,
|
||||
POLLING_TOKEN_ENVIRONMENT_TYPES,
|
||||
MESSAGE_TYPE,
|
||||
} from '../../shared/constants/app';
|
||||
import { hasUnconfirmedTransactions } from '../helpers/utils/confirm-tx.util';
|
||||
import txHelper from '../helpers/utils/tx-helper';
|
||||
@ -1021,6 +1022,96 @@ export function cancelMsg(msgData) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels all of the given messages
|
||||
*
|
||||
* @param {Array<object>} msgDataList - a list of msg data objects
|
||||
* @returns {function(*): Promise<void>}
|
||||
*/
|
||||
export function cancelMsgs(msgDataList) {
|
||||
return async (dispatch) => {
|
||||
dispatch(showLoadingIndication());
|
||||
|
||||
try {
|
||||
const msgIds = msgDataList.map((id) => id);
|
||||
const cancellations = msgDataList.map(
|
||||
({ id, type }) =>
|
||||
new Promise((resolve, reject) => {
|
||||
switch (type) {
|
||||
case MESSAGE_TYPE.ETH_SIGN_TYPED_DATA:
|
||||
background.cancelTypedMessage(id, (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
return;
|
||||
case MESSAGE_TYPE.PERSONAL_SIGN:
|
||||
background.cancelPersonalMessage(id, (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
return;
|
||||
case MESSAGE_TYPE.ETH_DECRYPT:
|
||||
background.cancelDecryptMessage(id, (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
return;
|
||||
case MESSAGE_TYPE.ETH_GET_ENCRYPTION_PUBLIC_KEY:
|
||||
background.cancelEncryptionPublicKeyMsg(id, (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
return;
|
||||
case MESSAGE_TYPE.ETH_SIGN:
|
||||
background.cancelMessage(id, (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
return;
|
||||
default:
|
||||
reject(
|
||||
new Error(
|
||||
`MetaMask Message Signature: Unknown message type: ${id}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
await Promise.all(cancellations);
|
||||
const newState = await updateMetamaskStateFromBackground();
|
||||
dispatch(updateMetamaskState(newState));
|
||||
|
||||
msgIds.forEach((id) => {
|
||||
dispatch(completedTx(id));
|
||||
});
|
||||
} catch (err) {
|
||||
log.error(err);
|
||||
} finally {
|
||||
if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
|
||||
closeNotificationPopup();
|
||||
} else {
|
||||
dispatch(hideLoadingIndication());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function cancelPersonalMsg(msgData) {
|
||||
return async (dispatch) => {
|
||||
dispatch(showLoadingIndication());
|
||||
|
@ -1696,4 +1696,58 @@ describe('Actions', () => {
|
||||
expect(expectedAction.value.id).toStrictEqual(txId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#cancelMsgs', () => {
|
||||
it('creates COMPLETED_TX with the cancelled messages IDs', async () => {
|
||||
const store = mockStore();
|
||||
|
||||
const cancelTypedMessageStub = sinon.stub().callsFake((_, cb) => cb());
|
||||
|
||||
const cancelPersonalMessageStub = sinon.stub().callsFake((_, cb) => cb());
|
||||
|
||||
background.getApi.returns({
|
||||
cancelTypedMessage: cancelTypedMessageStub,
|
||||
cancelPersonalMessage: cancelPersonalMessageStub,
|
||||
getState: sinon.stub().callsFake((cb) =>
|
||||
cb(null, {
|
||||
currentLocale: 'test',
|
||||
selectedAddress: '0xFirstAddress',
|
||||
provider: {
|
||||
chainId: '0x1',
|
||||
},
|
||||
accounts: {
|
||||
'0xFirstAddress': {
|
||||
balance: '0x0',
|
||||
},
|
||||
},
|
||||
cachedBalances: {
|
||||
'0x1': {
|
||||
'0xFirstAddress': '0x0',
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
const msgsList = [
|
||||
{ id: 7648683973086304, status: 'unapproved', type: 'personal_sign' },
|
||||
{
|
||||
id: 7648683973086303,
|
||||
status: 'unapproved',
|
||||
type: 'eth_signTypedData',
|
||||
},
|
||||
];
|
||||
|
||||
actions._setBackgroundConnection(background.getApi());
|
||||
|
||||
await store.dispatch(actions.cancelMsgs(msgsList));
|
||||
const resultantActions = store.getActions();
|
||||
const expectedActions = resultantActions.filter(
|
||||
(action) => action.type === 'COMPLETED_TX',
|
||||
);
|
||||
|
||||
expect(expectedActions[0].value.id).toStrictEqual(msgsList[0]);
|
||||
expect(expectedActions[1].value.id).toStrictEqual(msgsList[1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user