mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 01:39:44 +01:00
Prevent account name collisions (#16752)
* dealt with most of the problems in the Create Account dialog * Fixed "newAccountNumberName" localizations * In another language, don't allow accounts named, for instance, Cuenta 3 * Editing an account name later now follows the same rules * Fixing lint errors * Responding to the review by @adonesky1 * Worked with @montelaidev to alter the RegExp, in order to catch spaces before and after the account name * Fixed line breaks for eslint
This commit is contained in:
parent
3cf5ef642f
commit
b9d9112b97
2
app/_locales/am/messages.json
generated
2
app/_locales/am/messages.json
generated
@ -544,7 +544,7 @@
|
|||||||
"message": "አዲስ መለያ"
|
"message": "አዲስ መለያ"
|
||||||
},
|
},
|
||||||
"newAccountNumberName": {
|
"newAccountNumberName": {
|
||||||
"message": "መለያ$1",
|
"message": "መለያ $1",
|
||||||
"description": "Default name of next account to be created on create account screen"
|
"description": "Default name of next account to be created on create account screen"
|
||||||
},
|
},
|
||||||
"newContact": {
|
"newContact": {
|
||||||
|
4
app/_locales/en/messages.json
generated
4
app/_locales/en/messages.json
generated
@ -144,6 +144,10 @@
|
|||||||
"message": "This account name already exists",
|
"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"
|
"description": "This is an error message shown when the user enters a new account name that matches an existing account name"
|
||||||
},
|
},
|
||||||
|
"accountNameReserved": {
|
||||||
|
"message": "This account name is reserved",
|
||||||
|
"description": "This is an error message shown when the user enters a new account name that is reserved for future use"
|
||||||
|
},
|
||||||
"accountOptions": {
|
"accountOptions": {
|
||||||
"message": "Account options"
|
"message": "Account options"
|
||||||
},
|
},
|
||||||
|
2
app/_locales/fa/messages.json
generated
2
app/_locales/fa/messages.json
generated
@ -559,7 +559,7 @@
|
|||||||
"message": "حساب جدید"
|
"message": "حساب جدید"
|
||||||
},
|
},
|
||||||
"newAccountNumberName": {
|
"newAccountNumberName": {
|
||||||
"message": "حساب 1$1",
|
"message": "حساب $1",
|
||||||
"description": "Default name of next account to be created on create account screen"
|
"description": "Default name of next account to be created on create account screen"
|
||||||
},
|
},
|
||||||
"newContact": {
|
"newContact": {
|
||||||
|
2
app/_locales/no/messages.json
generated
2
app/_locales/no/messages.json
generated
@ -546,7 +546,7 @@
|
|||||||
"message": "Ny konto "
|
"message": "Ny konto "
|
||||||
},
|
},
|
||||||
"newAccountNumberName": {
|
"newAccountNumberName": {
|
||||||
"message": "Konto $1 ",
|
"message": "Konto $1",
|
||||||
"description": "Default name of next account to be created on create account screen"
|
"description": "Default name of next account to be created on create account screen"
|
||||||
},
|
},
|
||||||
"newContact": {
|
"newContact": {
|
||||||
|
2
app/_locales/ta/messages.json
generated
2
app/_locales/ta/messages.json
generated
@ -282,7 +282,7 @@
|
|||||||
"message": "புதிய கணக்கு"
|
"message": "புதிய கணக்கு"
|
||||||
},
|
},
|
||||||
"newAccountNumberName": {
|
"newAccountNumberName": {
|
||||||
"message": "கணக்கு $ 1",
|
"message": "கணக்கு $1",
|
||||||
"description": "Default name of next account to be created on create account screen"
|
"description": "Default name of next account to be created on create account screen"
|
||||||
},
|
},
|
||||||
"newContract": {
|
"newContract": {
|
||||||
|
@ -41,8 +41,8 @@
|
|||||||
"sentry:publish": "node ./development/sentry-publish.js",
|
"sentry:publish": "node ./development/sentry-publish.js",
|
||||||
"lint": "yarn lint:prettier && yarn lint:eslint && yarn lint:tsc && yarn lint:styles",
|
"lint": "yarn lint:prettier && yarn lint:eslint && yarn lint:tsc && yarn lint:styles",
|
||||||
"lint:fix": "yarn lint:prettier:fix && yarn lint:eslint:fix && yarn lint:styles:fix",
|
"lint:fix": "yarn lint:prettier:fix && yarn lint:eslint:fix && yarn lint:styles:fix",
|
||||||
"lint:prettier": "prettier '**/*.json' --check",
|
"lint:prettier": "prettier --check -- **/*.json",
|
||||||
"lint:prettier:fix": "prettier '**/*.json' --write",
|
"lint:prettier:fix": "prettier --write -- **/*.json",
|
||||||
"lint:changed": "{ git ls-files --others --exclude-standard ; git diff-index --name-only --diff-filter=d HEAD ; } | grep --regexp='[.]js$' | tr '\\n' '\\0' | xargs -0 eslint",
|
"lint:changed": "{ git ls-files --others --exclude-standard ; git diff-index --name-only --diff-filter=d HEAD ; } | grep --regexp='[.]js$' | tr '\\n' '\\0' | xargs -0 eslint",
|
||||||
"lint:changed:fix": "{ git ls-files --others --exclude-standard ; git diff-index --name-only --diff-filter=d HEAD ; } | grep --regexp='[.]js$' | tr '\\n' '\\0' | xargs -0 eslint --fix",
|
"lint:changed:fix": "{ git ls-files --others --exclude-standard ; git diff-index --name-only --diff-filter=d HEAD ; } | grep --regexp='[.]js$' | tr '\\n' '\\0' | xargs -0 eslint --fix",
|
||||||
"lint:changelog": "auto-changelog validate",
|
"lint:changelog": "auto-changelog validate",
|
||||||
@ -53,7 +53,7 @@
|
|||||||
"lint:lockfile:dedupe:fix": "yarn dedupe",
|
"lint:lockfile:dedupe:fix": "yarn dedupe",
|
||||||
"lint:lockfile": "lockfile-lint --path yarn.lock --allowed-hosts npm yarn github.com codeload.github.com --empty-hostname true --allowed-schemes \"https:\" \"git+https:\" \"npm:\" \"patch:\" \"workspace:\"",
|
"lint:lockfile": "lockfile-lint --path yarn.lock --allowed-hosts npm yarn github.com codeload.github.com --empty-hostname true --allowed-schemes \"https:\" \"git+https:\" \"npm:\" \"patch:\" \"workspace:\"",
|
||||||
"lint:shellcheck": "./development/shellcheck.sh",
|
"lint:shellcheck": "./development/shellcheck.sh",
|
||||||
"lint:styles": "stylelint '*/**/*.scss'",
|
"lint:styles": "stylelint -- */**/*.scss",
|
||||||
"lint:styles:fix": "yarn lint:styles --fix",
|
"lint:styles:fix": "yarn lint:styles --fix",
|
||||||
"lint:tsc": "tsc --project tsconfig.json --noEmit",
|
"lint:tsc": "tsc --project tsconfig.json --noEmit",
|
||||||
"validate-source-maps": "node ./development/sourcemap-validator.js",
|
"validate-source-maps": "node ./development/sourcemap-validator.js",
|
||||||
|
@ -41,7 +41,6 @@ export default class AccountDetailsModal extends Component {
|
|||||||
setAccountLabel,
|
setAccountLabel,
|
||||||
keyrings,
|
keyrings,
|
||||||
rpcPrefs,
|
rpcPrefs,
|
||||||
accounts,
|
|
||||||
history,
|
history,
|
||||||
hideModal,
|
hideModal,
|
||||||
blockExplorerLinkText,
|
blockExplorerLinkText,
|
||||||
@ -52,12 +51,6 @@ export default class AccountDetailsModal extends Component {
|
|||||||
return kr.accounts.includes(address);
|
return kr.accounts.includes(address);
|
||||||
});
|
});
|
||||||
|
|
||||||
const getAccountsNames = (allAccounts, currentName) => {
|
|
||||||
return Object.values(allAccounts)
|
|
||||||
.map((item) => item.name)
|
|
||||||
.filter((itemName) => itemName !== currentName);
|
|
||||||
};
|
|
||||||
|
|
||||||
let exportPrivateKeyFeatureEnabled = true;
|
let exportPrivateKeyFeatureEnabled = true;
|
||||||
// This feature is disabled for hardware wallets
|
// This feature is disabled for hardware wallets
|
||||||
if (isHardwareKeyring(keyring?.type)) {
|
if (isHardwareKeyring(keyring?.type)) {
|
||||||
@ -91,7 +84,7 @@ export default class AccountDetailsModal extends Component {
|
|||||||
className="account-details-modal__name"
|
className="account-details-modal__name"
|
||||||
defaultValue={name}
|
defaultValue={name}
|
||||||
onSubmit={(label) => setAccountLabel(address, label)}
|
onSubmit={(label) => setAccountLabel(address, label)}
|
||||||
accountsNames={getAccountsNames(accounts, name)}
|
accounts={this.props.accounts}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<QrView
|
<QrView
|
||||||
@ -111,15 +104,12 @@ export default class AccountDetailsModal extends Component {
|
|||||||
: openBlockExplorer
|
: openBlockExplorer
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{this.context.t(
|
{this.context.t(blockExplorerLinkText.firstPart, [
|
||||||
blockExplorerLinkText.firstPart,
|
blockExplorerLinkText.secondPart,
|
||||||
blockExplorerLinkText.secondPart === ''
|
])}
|
||||||
? null
|
|
||||||
: [blockExplorerLinkText.secondPart],
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{exportPrivateKeyFeatureEnabled ? (
|
{exportPrivateKeyFeatureEnabled && (
|
||||||
<Button
|
<Button
|
||||||
type="secondary"
|
type="secondary"
|
||||||
className="account-details-modal__button"
|
className="account-details-modal__button"
|
||||||
@ -137,7 +127,7 @@ export default class AccountDetailsModal extends Component {
|
|||||||
>
|
>
|
||||||
{this.context.t('exportPrivateKey')}
|
{this.context.t('exportPrivateKey')}
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
)}
|
||||||
</AccountModalContainer>
|
</AccountModalContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import { getAccountNameErrorMessage } from '../../../helpers/utils/accounts';
|
||||||
|
|
||||||
class EditableLabel extends Component {
|
export default class EditableLabel extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onSubmit: PropTypes.func.isRequired,
|
onSubmit: PropTypes.func.isRequired,
|
||||||
defaultValue: PropTypes.string,
|
defaultValue: PropTypes.string,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
accountsNames: PropTypes.array,
|
accounts: PropTypes.array,
|
||||||
};
|
};
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
@ -19,91 +20,71 @@ class EditableLabel extends Component {
|
|||||||
value: this.props.defaultValue || '',
|
value: this.props.defaultValue || '',
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSubmit() {
|
async handleSubmit(isValidAccountName) {
|
||||||
const { value } = this.state;
|
if (!isValidAccountName) {
|
||||||
const { accountsNames } = this.props;
|
|
||||||
|
|
||||||
if (value === '' || accountsNames.includes(value)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise.resolve(this.props.onSubmit(value)).then(() =>
|
await this.props.onSubmit(this.state.value);
|
||||||
this.setState({ isEditing: false }),
|
this.setState({ isEditing: false });
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderEditing() {
|
renderEditing() {
|
||||||
const { value } = this.state;
|
const { isValidAccountName, errorMessage } = getAccountNameErrorMessage(
|
||||||
const { accountsNames } = this.props;
|
this.props.accounts,
|
||||||
|
this.context,
|
||||||
|
this.state.value,
|
||||||
|
this.props.defaultValue,
|
||||||
|
);
|
||||||
|
|
||||||
return [
|
return (
|
||||||
<input
|
<div className={classnames('editable-label', this.props.className)}>
|
||||||
key={1}
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
dir="auto"
|
dir="auto"
|
||||||
value={this.state.value}
|
value={this.state.value}
|
||||||
onKeyPress={(event) => {
|
onKeyPress={(event) => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
this.handleSubmit();
|
this.handleSubmit(isValidAccountName);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onChange={(event) => this.setState({ value: event.target.value })}
|
onChange={(event) => this.setState({ value: event.target.value })}
|
||||||
data-testid="editable-input"
|
data-testid="editable-input"
|
||||||
className={classnames('large-input', 'editable-label__input', {
|
className={classnames('large-input', 'editable-label__input', {
|
||||||
'editable-label__input--error':
|
'editable-label__input--error': !isValidAccountName,
|
||||||
value === '' || accountsNames.includes(value),
|
})}
|
||||||
})}
|
autoFocus
|
||||||
autoFocus
|
/>
|
||||||
/>,
|
<button
|
||||||
<button
|
className="editable-label__icon-button"
|
||||||
className="editable-label__icon-button"
|
onClick={() => this.handleSubmit(isValidAccountName)}
|
||||||
key={2}
|
>
|
||||||
onClick={() => this.handleSubmit()}
|
<i className="fa fa-check editable-label__icon" />
|
||||||
>
|
</button>
|
||||||
<i className="fa fa-check editable-label__icon" />
|
<div className="editable-label__error editable-label__error-amount">
|
||||||
</button>,
|
{errorMessage}
|
||||||
];
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderReadonly() {
|
renderReadonly() {
|
||||||
return [
|
return (
|
||||||
<div key={1} className="editable-label__value">
|
<div className={classnames('editable-label', this.props.className)}>
|
||||||
{this.state.value}
|
<div className="editable-label__value">{this.state.value}</div>
|
||||||
</div>,
|
<button
|
||||||
<button
|
className="editable-label__icon-button"
|
||||||
key={2}
|
data-testid="editable-label-button"
|
||||||
className="editable-label__icon-button"
|
onClick={() => this.setState({ isEditing: true })}
|
||||||
data-testid="editable-label-button"
|
>
|
||||||
onClick={() => this.setState({ isEditing: true })}
|
<i className="fas fa-pencil-alt editable-label__icon" />
|
||||||
>
|
</button>
|
||||||
<i className="fas fa-pencil-alt editable-label__icon" />
|
</div>
|
||||||
</button>,
|
);
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isEditing, value } = this.state;
|
return this.state.isEditing ? this.renderEditing() : this.renderReadonly();
|
||||||
const { className, accountsNames } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<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}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EditableLabel;
|
|
||||||
|
@ -14,13 +14,20 @@ export default {
|
|||||||
className: {
|
className: {
|
||||||
control: 'text',
|
control: 'text',
|
||||||
},
|
},
|
||||||
accountsNames: {
|
accounts: {
|
||||||
control: 'array',
|
control: 'array',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
defaultValue: 'Account 3',
|
defaultValue: 'Account 3',
|
||||||
accountsNames: ['Account 1', 'Account 2'],
|
accounts: [
|
||||||
|
{
|
||||||
|
name: 'Account 1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Account 2',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
flex-flow: wrap;
|
||||||
|
|
||||||
&__value {
|
&__value {
|
||||||
max-width: 250px;
|
max-width: 250px;
|
||||||
@ -26,7 +27,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__icon-button {
|
&__icon-button {
|
||||||
position: absolute;
|
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
left: 100%;
|
left: 100%;
|
||||||
background: unset;
|
background: unset;
|
||||||
@ -42,6 +42,8 @@
|
|||||||
|
|
||||||
left: 8px;
|
left: 8px;
|
||||||
color: var(--color-error-default);
|
color: var(--color-error-default);
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__error-amount {
|
&__error-amount {
|
||||||
|
37
ui/helpers/utils/accounts.js
Normal file
37
ui/helpers/utils/accounts.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
export function getAccountNameErrorMessage(
|
||||||
|
accounts,
|
||||||
|
context,
|
||||||
|
newAccountName,
|
||||||
|
defaultAccountName,
|
||||||
|
) {
|
||||||
|
const isDuplicateAccountName = accounts.some(
|
||||||
|
(item) => item.name === newAccountName,
|
||||||
|
);
|
||||||
|
|
||||||
|
const localizedWordForAccount = context
|
||||||
|
.t('newAccountNumberName')
|
||||||
|
.replace(' $1', '');
|
||||||
|
|
||||||
|
// Match strings starting with ${localizedWordForAccount} and then any numeral, case insensitive
|
||||||
|
// Trim spaces before and after
|
||||||
|
const reservedRegEx = new RegExp(
|
||||||
|
`^\\s*${localizedWordForAccount} \\d+\\s*$`,
|
||||||
|
'iu',
|
||||||
|
);
|
||||||
|
const isReservedAccountName = reservedRegEx.test(newAccountName);
|
||||||
|
|
||||||
|
const isValidAccountName =
|
||||||
|
newAccountName === defaultAccountName || // What is written in the text field is the same as the placeholder
|
||||||
|
(!isDuplicateAccountName && !isReservedAccountName);
|
||||||
|
|
||||||
|
let errorMessage;
|
||||||
|
if (isValidAccountName) {
|
||||||
|
errorMessage = '\u200d'; // This is Unicode for an invisible character, so the spacing stays constant
|
||||||
|
} else if (isDuplicateAccountName) {
|
||||||
|
errorMessage = context.t('accountNameDuplicate');
|
||||||
|
} else if (isReservedAccountName) {
|
||||||
|
errorMessage = context.t('accountNameReserved');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { isValidAccountName, errorMessage };
|
||||||
|
}
|
@ -84,13 +84,18 @@
|
|||||||
border: 1px solid var(--color-border-muted);
|
border: 1px solid var(--color-border-muted);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-color: var(--color-background-default);
|
background-color: var(--color-background-default);
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-default);
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
|
|
||||||
&__error {
|
&__error {
|
||||||
border: 1px solid var(--color-error-alternative);
|
border: 1px solid var(--color-error-alternative);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__error {
|
&__error {
|
||||||
@ -105,7 +110,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__buttons {
|
&__buttons {
|
||||||
margin-top: 39px;
|
margin-top: 22px;
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import Button from '../../components/ui/button';
|
import Button from '../../components/ui/button';
|
||||||
import { EVENT, EVENT_NAMES } from '../../../shared/constants/metametrics';
|
import { EVENT, EVENT_NAMES } from '../../../shared/constants/metametrics';
|
||||||
|
import { getAccountNameErrorMessage } from '../../helpers/utils/accounts';
|
||||||
|
|
||||||
export default class NewAccountCreateForm extends Component {
|
export default class NewAccountCreateForm extends Component {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
@ -46,11 +47,12 @@ export default class NewAccountCreateForm extends Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const accountNameExists = (allAccounts, accountName) => {
|
const { isValidAccountName, errorMessage } = getAccountNameErrorMessage(
|
||||||
return Boolean(allAccounts.find((item) => item.name === accountName));
|
accounts,
|
||||||
};
|
this.context,
|
||||||
|
newAccountName,
|
||||||
const existingAccountName = accountNameExists(accounts, newAccountName);
|
defaultAccountName,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="new-account-create-form">
|
<div className="new-account-create-form">
|
||||||
@ -59,8 +61,9 @@ export default class NewAccountCreateForm extends Component {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
className={classnames('new-account-create-form__input', {
|
className={classnames({
|
||||||
'new-account-create-form__input__error': existingAccountName,
|
'new-account-create-form__input': true,
|
||||||
|
'new-account-create-form__input__error': !isValidAccountName,
|
||||||
})}
|
})}
|
||||||
value={newAccountName}
|
value={newAccountName}
|
||||||
placeholder={defaultAccountName}
|
placeholder={defaultAccountName}
|
||||||
@ -69,16 +72,9 @@ export default class NewAccountCreateForm extends Component {
|
|||||||
}
|
}
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
{existingAccountName ? (
|
<div className="new-account-create-form__error new-account-create-form__error-amount">
|
||||||
<div
|
{errorMessage}
|
||||||
className={classnames(
|
</div>
|
||||||
' new-account-create-form__error',
|
|
||||||
' new-account-create-form__error-amount',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{this.context.t('accountNameDuplicate')}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<div className="new-account-create-form__buttons">
|
<div className="new-account-create-form__buttons">
|
||||||
<Button
|
<Button
|
||||||
type="secondary"
|
type="secondary"
|
||||||
@ -93,7 +89,7 @@ export default class NewAccountCreateForm extends Component {
|
|||||||
large
|
large
|
||||||
className="new-account-create-form__button"
|
className="new-account-create-form__button"
|
||||||
onClick={createClick}
|
onClick={createClick}
|
||||||
disabled={existingAccountName}
|
disabled={!isValidAccountName}
|
||||||
>
|
>
|
||||||
{this.context.t('create')}
|
{this.context.t('create')}
|
||||||
</Button>
|
</Button>
|
||||||
|
Loading…
Reference in New Issue
Block a user