Create.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. // Copyright 2017-2019 @polkadot/app-accounts authors & contributors
  2. // This software may be modified and distributed under the terms
  3. // of the Apache-2.0 license. See the LICENSE file for details.
  4. import { I18nProps } from '@polkadot/react-components/types';
  5. import { KeypairType } from '@polkadot/util-crypto/types';
  6. import { ModalProps } from '../types';
  7. import { useMyAccount } from '@polkadot/joy-utils/MyAccountContext';
  8. import { generateSeed, updateAddress, createAccount, isPasswordValid, AddressState, SeedType } from '@polkadot/joy-utils/accounts';
  9. import React, { useContext, useState } from 'react';
  10. import styled from 'styled-components';
  11. import { ApiContext } from '@polkadot/react-api';
  12. import { AddressRow, Button, Dropdown, Input, Modal, Password } from '@polkadot/react-components';
  13. import uiSettings from '@polkadot/ui-settings';
  14. import translate from '../translate';
  15. import CreateConfirmation from './CreateConfirmation';
  16. interface Props extends ModalProps, I18nProps {
  17. seed?: string;
  18. type?: KeypairType;
  19. }
  20. function Create ({ className, onClose, onStatusChange, seed: propsSeed, t, type: propsType }: Props): React.ReactElement<Props> {
  21. const { isDevelopment } = useContext(ApiContext);
  22. const [{ address, deriveError, derivePath, isSeedValid, pairType, seed, seedType }, setAddress] = useState<AddressState>(generateSeed(propsSeed, '', propsSeed ? 'raw' : 'bip', propsType));
  23. const [isConfirmationOpen, setIsConfirmationOpen] = useState(false);
  24. const [{ isNameValid, name }, setName] = useState({ isNameValid: false, name: '' });
  25. const [{ isPassValid, password }, setPassword] = useState({ isPassValid: false, password: '' });
  26. const isValid = !!address && !deriveError && isNameValid && isPassValid && isSeedValid;
  27. const _onChangePass = (password: string): void =>
  28. setPassword({ isPassValid: isPasswordValid(password), password });
  29. const _onChangeDerive = (newDerivePath: string): void =>
  30. setAddress(updateAddress(seed, newDerivePath, seedType, pairType));
  31. const _onChangeSeed = (newSeed: string): void =>
  32. setAddress(updateAddress(newSeed, derivePath, seedType, pairType));
  33. const _onChangePairType = (newPairType: KeypairType): void =>
  34. setAddress(updateAddress(seed, derivePath, seedType, newPairType));
  35. const _selectSeedType = (newSeedType: SeedType): void => {
  36. if (newSeedType !== seedType) {
  37. setAddress(generateSeed(null, derivePath, newSeedType, pairType));
  38. }
  39. };
  40. const _onChangeName = (name: string): void => setName({ isNameValid: !!name.trim(), name });
  41. const _toggleConfirmation = (): void => setIsConfirmationOpen(!isConfirmationOpen);
  42. const context = useMyAccount();
  43. const _onCommit = (): void => {
  44. if (!isValid) {
  45. return;
  46. }
  47. const status = createAccount(`${seed}${derivePath}`, pairType, name, password, t('created account'));
  48. context.set(status.account as string);
  49. _toggleConfirmation();
  50. onStatusChange(status);
  51. onClose();
  52. };
  53. return (
  54. <Modal
  55. className={className}
  56. dimmer='inverted'
  57. open
  58. >
  59. <Modal.Header>{t('Add an account via seed')}</Modal.Header>
  60. {address && isConfirmationOpen && (
  61. <CreateConfirmation
  62. address={address}
  63. name={name}
  64. onCommit={_onCommit}
  65. onClose={_toggleConfirmation}
  66. />
  67. )}
  68. <Modal.Content>
  69. <AddressRow
  70. defaultName={name}
  71. noDefaultNameOpacity
  72. value={isSeedValid ? address : ''}
  73. >
  74. <Input
  75. autoFocus
  76. className='full'
  77. help={t('Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g "name_you_want - stash".')}
  78. isError={!isNameValid}
  79. label={t('name')}
  80. onChange={_onChangeName}
  81. onEnter={_onCommit}
  82. placeholder={t('new account')}
  83. value={name}
  84. />
  85. <Input
  86. className='full'
  87. help={t('The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the "--key" parameter of your node.')}
  88. isAction
  89. isError={!isSeedValid}
  90. isReadOnly={seedType === 'dev'}
  91. label={
  92. seedType === 'bip'
  93. ? t('mnemonic seed')
  94. : seedType === 'dev'
  95. ? t('development seed')
  96. : t('seed (hex or string)')
  97. }
  98. onChange={_onChangeSeed}
  99. onEnter={_onCommit}
  100. value={seed}
  101. >
  102. <Dropdown
  103. isButton
  104. defaultValue={seedType}
  105. onChange={_selectSeedType}
  106. options={
  107. (
  108. isDevelopment
  109. ? [{ value: 'dev', text: t('Development') }]
  110. : []
  111. ).concat(
  112. { value: 'bip', text: t('Mnemonic') },
  113. { value: 'raw', text: t('Raw seed') }
  114. )
  115. }
  116. />
  117. </Input>
  118. <Password
  119. className='full'
  120. help={t('This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).')}
  121. isError={!isPassValid}
  122. label={t('password')}
  123. onChange={_onChangePass}
  124. onEnter={_onCommit}
  125. value={password}
  126. />
  127. <details
  128. className='accounts--Creator-advanced'
  129. open
  130. >
  131. <summary>{t('Advanced creation options')}</summary>
  132. <Dropdown
  133. defaultValue={pairType}
  134. help={t('Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use "ed25519".')}
  135. label={t('keypair crypto type')}
  136. onChange={_onChangePairType}
  137. options={uiSettings.availableCryptos}
  138. />
  139. <Input
  140. className='full'
  141. help={t('You can set a custom derivation path for this account using the following syntax "/<soft-key>//<hard-key>///<password>". The "/<soft-key>" and "//<hard-key>" may be repeated and mixed`. The "///password" is optional and should only occur once.')}
  142. isError={!!deriveError}
  143. label={t('secret derivation path')}
  144. onChange={_onChangeDerive}
  145. onEnter={_onCommit}
  146. placeholder={t('//hard/soft///password')}
  147. value={derivePath}
  148. />
  149. {deriveError && (
  150. <article className='error'>{deriveError}</article>
  151. )}
  152. </details>
  153. </AddressRow>
  154. </Modal.Content>
  155. <Modal.Actions>
  156. <Button.Group>
  157. <Button
  158. icon='cancel'
  159. isNegative
  160. label={t('Cancel')}
  161. onClick={onClose}
  162. />
  163. <Button.Or />
  164. <Button
  165. icon='plus'
  166. isDisabled={!isValid}
  167. isPrimary
  168. label={t('Save')}
  169. onClick={_toggleConfirmation}
  170. />
  171. </Button.Group>
  172. </Modal.Actions>
  173. </Modal>
  174. );
  175. }
  176. export default translate(
  177. styled(Create)`
  178. .accounts--Creator-advanced {
  179. margin-top: 1rem;
  180. }
  181. `
  182. );