mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 18:00:18 +01:00
Fix account name duplicates (#12867)
* Fix account name duplicates * Change button disabling logic * Fix function error * Requested changes * Move functions from selectors file into components * Applying requested changes
This commit is contained in:
parent
f946c030b5
commit
62f41600f2
@ -73,6 +73,10 @@
|
||||
"accountName": {
|
||||
"message": "Account Name"
|
||||
},
|
||||
"accountNameDuplicate": {
|
||||
"message": "This account name already exists",
|
||||
"description": "This is an error message shown when the user enters a new account name that matches an existing account name"
|
||||
},
|
||||
"accountOptions": {
|
||||
"message": "Account Options"
|
||||
},
|
||||
|
@ -17,6 +17,7 @@ export default class AccountDetailsModal extends Component {
|
||||
setAccountLabel: PropTypes.func,
|
||||
keyrings: PropTypes.array,
|
||||
rpcPrefs: PropTypes.object,
|
||||
accounts: PropTypes.array,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
@ -32,6 +33,7 @@ export default class AccountDetailsModal extends Component {
|
||||
setAccountLabel,
|
||||
keyrings,
|
||||
rpcPrefs,
|
||||
accounts,
|
||||
} = this.props;
|
||||
const { name, address } = selectedIdentity;
|
||||
|
||||
@ -39,6 +41,12 @@ export default class AccountDetailsModal extends Component {
|
||||
return kr.accounts.includes(address);
|
||||
});
|
||||
|
||||
const getAccountsNames = (allAccounts, currentName) => {
|
||||
return Object.values(allAccounts)
|
||||
.map((item) => item.name)
|
||||
.filter((itemName) => itemName !== currentName);
|
||||
};
|
||||
|
||||
let exportPrivateKeyFeatureEnabled = true;
|
||||
// This feature is disabled for hardware wallets
|
||||
if (isHardwareKeyring(keyring?.type)) {
|
||||
@ -51,6 +59,7 @@ export default class AccountDetailsModal extends Component {
|
||||
className="account-details-modal__name"
|
||||
defaultValue={name}
|
||||
onSubmit={(label) => setAccountLabel(address, label)}
|
||||
accountsNames={getAccountsNames(accounts, name)}
|
||||
/>
|
||||
|
||||
<QrView
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
getSelectedIdentity,
|
||||
getRpcPrefsForCurrentProvider,
|
||||
getCurrentChainId,
|
||||
getMetaMaskAccountsOrdered,
|
||||
} from '../../../../selectors';
|
||||
import AccountDetailsModal from './account-details-modal.component';
|
||||
|
||||
@ -13,6 +14,7 @@ const mapStateToProps = (state) => {
|
||||
selectedIdentity: getSelectedIdentity(state),
|
||||
keyrings: state.metamask.keyrings,
|
||||
rpcPrefs: getRpcPrefsForCurrentProvider(state),
|
||||
accounts: getMetaMaskAccountsOrdered(state),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -30,6 +30,12 @@ describe('Account Details Modal', () => {
|
||||
name: 'Account 1',
|
||||
},
|
||||
},
|
||||
accounts: {
|
||||
address: '0xAddress',
|
||||
lastSelected: 1637764711510,
|
||||
name: 'Account 1',
|
||||
balance: '0x543a5fb6caccf599',
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -7,6 +7,11 @@ class EditableLabel extends Component {
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
defaultValue: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
accountsNames: PropTypes.array,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
};
|
||||
|
||||
state = {
|
||||
@ -16,8 +21,9 @@ class EditableLabel extends Component {
|
||||
|
||||
handleSubmit() {
|
||||
const { value } = this.state;
|
||||
const { accountsNames } = this.props;
|
||||
|
||||
if (value === '') {
|
||||
if (value === '' || accountsNames.includes(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -28,6 +34,7 @@ class EditableLabel extends Component {
|
||||
|
||||
renderEditing() {
|
||||
const { value } = this.state;
|
||||
const { accountsNames } = this.props;
|
||||
|
||||
return [
|
||||
<input
|
||||
@ -43,7 +50,8 @@ class EditableLabel extends Component {
|
||||
}}
|
||||
onChange={(event) => this.setState({ value: event.target.value })}
|
||||
className={classnames('large-input', 'editable-label__input', {
|
||||
'editable-label__input--error': value === '',
|
||||
'editable-label__input--error':
|
||||
value === '' || accountsNames.includes(value),
|
||||
})}
|
||||
autoFocus
|
||||
/>,
|
||||
@ -73,13 +81,25 @@ class EditableLabel extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isEditing } = this.state;
|
||||
const { className } = this.props;
|
||||
const { isEditing, value } = this.state;
|
||||
const { className, accountsNames } = this.props;
|
||||
|
||||
return (
|
||||
<div className={classnames('editable-label', className)}>
|
||||
{isEditing ? this.renderEditing() : this.renderReadonly()}
|
||||
</div>
|
||||
<>
|
||||
<div className={classnames('editable-label', className)}>
|
||||
{isEditing ? this.renderEditing() : this.renderReadonly()}
|
||||
</div>
|
||||
{accountsNames.includes(value) ? (
|
||||
<div
|
||||
className={classnames(
|
||||
'editable-label__error',
|
||||
'editable-label__error-amount',
|
||||
)}
|
||||
>
|
||||
{this.context.t('accountNameDuplicate')}
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -34,4 +34,15 @@
|
||||
cursor: pointer;
|
||||
color: $dusty-gray;
|
||||
}
|
||||
|
||||
&__error {
|
||||
@include H7;
|
||||
|
||||
left: 8px;
|
||||
color: $red;
|
||||
}
|
||||
|
||||
&__error-amount {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +87,21 @@
|
||||
color: $scorpion;
|
||||
margin-top: 15px;
|
||||
padding: 0 20px;
|
||||
|
||||
&__error {
|
||||
border: 1px solid $error-3;
|
||||
}
|
||||
}
|
||||
|
||||
&__error {
|
||||
@include H7;
|
||||
|
||||
left: 8px;
|
||||
color: $red;
|
||||
}
|
||||
|
||||
&__error-amount {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import Button from '../../components/ui/button';
|
||||
|
||||
export default class NewAccountCreateForm extends Component {
|
||||
@ -16,7 +17,13 @@ export default class NewAccountCreateForm extends Component {
|
||||
|
||||
render() {
|
||||
const { newAccountName, defaultAccountName } = this.state;
|
||||
const { history, createAccount, mostRecentOverviewPage } = this.props;
|
||||
const {
|
||||
history,
|
||||
createAccount,
|
||||
mostRecentOverviewPage,
|
||||
accounts,
|
||||
} = this.props;
|
||||
|
||||
const createClick = (_) => {
|
||||
createAccount(newAccountName || defaultAccountName)
|
||||
.then(() => {
|
||||
@ -43,6 +50,14 @@ export default class NewAccountCreateForm extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
const accountNameExists = (allAccounts, accountName) => {
|
||||
const accountsNames = allAccounts.map((item) => item.name);
|
||||
|
||||
return accountsNames.includes(accountName);
|
||||
};
|
||||
|
||||
const existingAccountName = accountNameExists(accounts, newAccountName);
|
||||
|
||||
return (
|
||||
<div className="new-account-create-form">
|
||||
<div className="new-account-create-form__input-label">
|
||||
@ -50,7 +65,9 @@ export default class NewAccountCreateForm extends Component {
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
className="new-account-create-form__input"
|
||||
className={classnames('new-account-create-form__input', {
|
||||
'new-account-create-form__input__error': existingAccountName,
|
||||
})}
|
||||
value={newAccountName}
|
||||
placeholder={defaultAccountName}
|
||||
onChange={(event) =>
|
||||
@ -58,6 +75,16 @@ export default class NewAccountCreateForm extends Component {
|
||||
}
|
||||
autoFocus
|
||||
/>
|
||||
{existingAccountName ? (
|
||||
<div
|
||||
className={classnames(
|
||||
' new-account-create-form__error',
|
||||
' new-account-create-form__error-amount',
|
||||
)}
|
||||
>
|
||||
{this.context.t('accountNameDuplicate')}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="new-account-create-form__buttons">
|
||||
<Button
|
||||
type="secondary"
|
||||
@ -72,6 +99,7 @@ export default class NewAccountCreateForm extends Component {
|
||||
large
|
||||
className="new-account-create-form__button"
|
||||
onClick={createClick}
|
||||
disabled={existingAccountName}
|
||||
>
|
||||
{this.context.t('create')}
|
||||
</Button>
|
||||
@ -87,6 +115,7 @@ NewAccountCreateForm.propTypes = {
|
||||
newAccountNumber: PropTypes.number,
|
||||
history: PropTypes.object,
|
||||
mostRecentOverviewPage: PropTypes.string.isRequired,
|
||||
accounts: PropTypes.array,
|
||||
};
|
||||
|
||||
NewAccountCreateForm.contextTypes = {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { connect } from 'react-redux';
|
||||
import * as actions from '../../store/actions';
|
||||
import { getMostRecentOverviewPage } from '../../ducks/history/history';
|
||||
import { getMetaMaskAccountsOrdered } from '../../selectors';
|
||||
import NewAccountCreateForm from './new-account.component';
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
@ -13,6 +14,7 @@ const mapStateToProps = (state) => {
|
||||
return {
|
||||
newAccountNumber,
|
||||
mostRecentOverviewPage: getMostRecentOverviewPage(state),
|
||||
accounts: getMetaMaskAccountsOrdered(state),
|
||||
};
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user