Browse Source

Merge branch 'redux' of https://github.com/SuperNETorg/EasyDEX-GUI into merge-tx-info

all-modes
pbca26 8 years ago
parent
commit
af4052b3ca
  1. 4
      react/change.log
  2. 64
      react/src/components/dashboard/about/about.js
  3. 2
      react/src/components/dashboard/sendCoin/sendCoin.render.js
  4. 2
      react/src/components/dashboard/settings/settings.render.js
  5. 6
      react/src/components/dashboard/walletsBalance/walletsBalance.render.js
  6. 22
      react/src/components/dashboard/walletsNativeSyncProgress/walletsNativeSyncProgress.js
  7. 3
      react/src/components/dashboard/walletsNativeSyncProgress/walletsNativeSyncProgress.render.js
  8. 171
      react/src/components/login/login.js
  9. 216
      react/src/components/login/login.render.js
  10. 52
      react/src/components/login/login.scss
  11. 11
      react/src/translate/en.js
  12. 24
      react/src/util/crypto/passphrasegenerator.js

4
react/change.log

@ -15,6 +15,10 @@ front:
- added error message if coin is already running in another mode
- added explicit "new address generated" message
- added cli / rpc passphru
- seed type check
- seed extra space(s) check
- custom seed option
- copy seed button
back:
- added cli route

64
react/src/components/dashboard/about/about.js

@ -1,16 +1,72 @@
import React from 'react';
import React from "react";
class About extends React.Component {
render() {
return (
<div className="page margin-left-0">
<div className="page-content">
<h2>About Iguana</h2>
<p>Page content goes here</p>
<h2>About Agama</h2>
<p>Agama Wallet is a desktop app that you can use to manage multiple cryptocurrency wallets. When you set up a
wallet, you can configure it to operate in one of the following modes:
<ul>
<li>
<span className="font-weight-600">Basilisk Mode</span>: Doesn't download the blockchain. Slightly slower
transaction performance.
</li>
<li>
<span className="font-weight-600">Full Mode</span>: Downloads the full blockchain, which can take a
while. Good transaction performance.
</li>
<li>
<span className="font-weight-600">Native Mode</span>: Only available for a few currencies. Like 'Full
Mode' but provides advanced functionality.
</li>
</ul>
Agama includes the following capabilities:
<ul>
<li>
<span className="font-weight-600">InstantDEX</span>: Easily exchange cryptocurrencies via a
shapeshift-like service.
<a href="https://supernet.org/en/technology/whitepapers/easydex-a-practical-native-dex" target="_blank">
(EasyDEX A Practical Native DEX)
</a>
</li>
<li>
<span className="font-weight-600">Atomic Exporer</span>: A universal local explorer ensures you don't
have query information from a centralized
server.
</li>
</ul>
<span className="font-weight-600">
Note: Agama Wallet is still in development. It is safe to use,
but you should make proper backups. We do not recommend using it as the primarily wallet for your cryptocurrencies.
</span>
<br/>
<span className="font-weight-600">Testers</span>:
You can help us test Agama. Just <a target="_blank" href="https://supernet.org/en/products/agama-wallet">download and install the latest release</a>.
Then, report any bugs you encounter to our developers on the #testing-agama Slack channel.
Your help is greatly appreciated!
Agama also supports the following desktop apps:
<ul>
<li>
<span className="font-weight-600">Jumblr</span>: A decentralized Bitcoin blockchain tumbler for privacy
and lower fees.
</li>
<li>
<span className="font-weight-600">NativeDEX</span>
</li>
</ul>
</p>
</div>
</div>
);
}
}
export default About;
export default About;

2
react/src/components/dashboard/sendCoin/sendCoin.render.js

@ -235,6 +235,8 @@ export const SendCoinRender = function () {
name="amount"
placeholder="0.000"
autoComplete="off"
defaultValue={ this.state.amount }
value={ this.state.amount }
onChange={ this.updateInput } />
</div>
<div className="col-lg-6 form-group form-material">

2
react/src/components/dashboard/settings/settings.render.js

@ -310,7 +310,7 @@ export const SettingsRender = function() {
id="wifkeysPassphrase"
onChange={ this.updateInput } />
<i
className={ this.state.seedInputVisibility ? 'seed-toggle fa fa-eye-slash' : 'seed-toggle fa fa-eye' }
className={ !this.state.seedInputVisibility ? 'seed-toggle fa fa-eye-slash' : 'seed-toggle fa fa-eye' }
onClick={ this.toggleSeedInputVisibility }></i>
<label
className="floating-label"

6
react/src/components/dashboard/walletsBalance/walletsBalance.render.js

@ -5,8 +5,7 @@ const WalletsBalanceRender = function() {
return (
<div id="wallet-widgets">
<div className="col-xs-12">
<div className={ this.isNativeMode() || (this.isFullMode()
&& !this.isFullySynced()) ? 'col-xs-12' : 'col-xs-12 hide' }>
<div className={ this.isFullMode() && !this.isFullySynced() ? 'col-xs-12' : 'col-xs-12 hide' }>
<div className="alert alert-info alert-dismissible">
<button
className="close"
@ -32,8 +31,7 @@ const WalletsBalanceRender = function() {
</div>
</div>
<div className={ this.isNativeMode() ? 'col-lg-3 col-xs-12' :
this.isBasiliskMode() ? 'col-lg-4 col-xs-12' : 'col-lg-12 col-xs-12'}>
<div className={ this.isNativeMode() ? 'col-lg-3 col-xs-12' : this.isBasiliskMode() ? 'col-lg-4 col-xs-12' : 'col-lg-12 col-xs-12'}>
<div className="widget widget-shadow">
<div className="widget-content">

22
react/src/components/dashboard/walletsNativeSyncProgress/walletsNativeSyncProgress.js

@ -22,7 +22,8 @@ class WalletsNativeSyncProgress extends React.Component {
</div>
);
} else {
if (this.props.Dashboard.progress && this.props.Dashboard.progress.block) {
if (this.props.Dashboard.progress &&
this.props.Dashboard.progress.blocks) {
const syncPercentage = (parseFloat(parseInt(this.props.Dashboard.progress.blocks, 10) * 100 / parseInt(this.props.Dashboard.progress.longestchain, 10)).toFixed(2) + '%').replace('NaN', 0);
return (
@ -37,7 +38,7 @@ class WalletsNativeSyncProgress extends React.Component {
<div
className="progress-bar progress-bar-info progress-bar-striped active font-size-80-percent"
style={{ width: '100%' }}>
<span style={{ width: '100%' }}>Loading blocks...</span>
<span style={{ width: '100%' }}>Loading blocks...it can take up to 15 min to load blocks</span>
</div>
);
}
@ -47,7 +48,10 @@ class WalletsNativeSyncProgress extends React.Component {
renderActivatingBestChainProgress() {
if (this.props.Settings &&
this.props.Settings.debugLog) {
if (this.props.Settings.debugLog.indexOf('UpdateTip') > -1) {
console.log('debugLog');
if (this.props.Settings.debugLog.indexOf('UpdateTip') > -1 &&
!this.props.Dashboard.progress &&
!this.props.Dashboard.progress.blocks) {
const temp = this.props.Settings.debugLog.split(' ');
let currentBestChain;
let currentProgress;
@ -65,7 +69,7 @@ class WalletsNativeSyncProgress extends React.Component {
if (this.props.Dashboard.progress.remoteKMDNode &&
!this.props.Dashboard.progress.remoteKMDNode.blocks) {
return (
`: ${currentProgress}%`
`: ${currentProgress}% (activating)`
);
} else {
if (this.props.Dashboard.progress.remoteKMDNode &&
@ -75,7 +79,11 @@ class WalletsNativeSyncProgress extends React.Component {
);
}
}
} else if (this.props.Settings.debugLog.indexOf('Still rescanning') > -1) {
} else if (
this.props.Settings.debugLog.indexOf('Still rescanning') > -1 &&
!this.props.Dashboard.progress ||
!this.props.Dashboard.progress.blocks
) {
const temp = this.props.Settings.debugLog.split(' ');
let currentProgress;
@ -86,11 +94,11 @@ class WalletsNativeSyncProgress extends React.Component {
}
return (
`: ${currentProgress}%`
`: ${currentProgress}% (rescanning blocks)`
);
} else {
return (
<span>...</span>
<span> (downloading blocks)</span>
);
}
}

3
react/src/components/dashboard/walletsNativeSyncProgress/walletsNativeSyncProgress.render.js

@ -10,7 +10,8 @@ export const ChainActivationNotificationRender = function() {
<span>×</span>
</button>
<h4>
{ translate('INDEX.ACTIVATING_CHAIN') }{ this.renderActivatingBestChainProgress() }
{ translate('INDEX.ACTIVATING_CHAIN') }
{ this.renderActivatingBestChainProgress() }
</h4>
<p>{ this.renderLB('INDEX.KMD_STARTED') }</p>
</div>

171
react/src/components/login/login.js

@ -1,21 +1,20 @@
import React from 'react';
import { translate } from '../../translate/translate';
import {
toggleAddcoinModal,
iguanaWalletPassphrase,
iguanaActiveHandle,
startInterval,
stopInterval,
getDexCoins,
toggleSyncOnlyModal,
getSyncOnlyForks,
createNewWallet
createNewWallet,
triggerToaster
} from '../../actions/actionCreators';
import Store from '../../store';
import { PassPhraseGenerator } from '../../util/crypto/passphrasegenerator';
import {PassPhraseGenerator} from '../../util/crypto/passphrasegenerator';
import SwallModalRender from './swall-modal.render';
import LoginRender from './login.render';
import {translate} from '../../translate/translate';
const IGUNA_ACTIVE_HANDLE_TIMEOUT = 3000;
const IGUNA_ACTIVE_COINS_TIMEOUT = 10000;
@ -28,22 +27,53 @@ class Login extends React.Component {
activeLoginSection: 'activateCoin',
loginPassphrase: null,
seedInputVisibility: false,
loginPassPhraseSeedType: null,
bitsOption: 256,
randomSeed: PassPhraseGenerator.generatePassPhrase(256),
randomSeedConfirm: '',
isSeedConfirmError: false,
isSeedBlank: false,
displaySeedBackupModal: false,
customWalletSeed: false,
isCustomSeedWeak: false,
};
this.toggleActivateCoinForm = this.toggleActivateCoinForm.bind(this);
this.updateInput = this.updateInput.bind(this);
this.updateRegisterConfirmPassPhraseInput = this.updateRegisterConfirmPassPhraseInput.bind(this);
this.updateLoginPassPhraseInput = this.updateLoginPassPhraseInput.bind(this);
this.loginSeed = this.loginSeed.bind(this);
this.toggleSeedInputVisibility = this.toggleSeedInputVisibility.bind(this);
this.handleRegisterWallet = this.handleRegisterWallet.bind(this);
this.openSyncOnlyModal = this.openSyncOnlyModal.bind(this);
this.toggleSeedBackupModal = this.toggleSeedBackupModal.bind(this);
this.copyPassPhraseToClipboard = this.copyPassPhraseToClipboard.bind(this);
this.execWalletCreate = this.execWalletCreate.bind(this);
}
isCustomWalletSeed() {
return this.state.customWalletSeed;
}
toggleCustomWalletSeed() {
this.setState({
customWalletSeed: !this.state.customWalletSeed
}, () => {
// if customWalletSeed is set to false, regenerate the seed
if (!this.state.customWalletSeed) {
this.setState({
randomSeed: PassPhraseGenerator.generatePassPhrase(this.state.bitsOption),
isSeedConfirmError: false,
isSeedBlank: false
});
} else {
// if customWalletSeed is set to true, reset to seed to an empty string
this.setState({
randomSeed: '',
randomSeedConfirm: ''
});
}
});
}
openSyncOnlyModal() {
Store.dispatch(getSyncOnlyForks());
@ -74,21 +104,22 @@ class Login extends React.Component {
this.setState(Object.assign({}, this.state, {
randomSeed: PassPhraseGenerator.generatePassPhrase(bits),
bitsOption: bits,
isSeedBlank: false
}));
}
componentWillReceiveProps(props) {
if (props &&
props.Main &&
props.Main.isLoggedIn) {
props.Main &&
props.Main.isLoggedIn) {
this.setState({
display: false,
});
}
if (props &&
props.Main &&
!props.Main.isLoggedIn) {
props.Main &&
!props.Main.isLoggedIn) {
this.setState({
display: true,
});
@ -105,8 +136,8 @@ class Login extends React.Component {
if (this.state.activeLoginSection !== 'signup') {
if (props &&
props.Main &&
props.Main.activeCoins) {
props.Main &&
props.Main.activeCoins) {
this.setState({
activeLoginSection: 'login',
});
@ -122,23 +153,84 @@ class Login extends React.Component {
Store.dispatch(toggleAddcoinModal(true, false));
}
updateInput(e) {
updateLoginPassPhraseInput(e) {
// remove any empty chars from the start/end of the string
const newValue = e.target.value ? e.target.value.trim() : null;
this.setState({
[e.target.name]: newValue,
loginPassPhraseSeedType: this.getLoginPassPhraseSeedType(newValue)
});
}
updateRegisterConfirmPassPhraseInput(e) {
this.setState({
[e.target.name]: e.target.value,
isSeedConfirmError: false,
isSeedBlank: this.isBlank(e.target.value)
});
}
updateWalletSeed(e) {
this.setState({
randomSeed: e.target.value,
isSeedConfirmError: false,
isSeedBlank: this.isBlank(e.target.value)
});
}
loginSeed() {
// reset the login pass phrase values so that when the user logs out, the values are clear
this.setState({
loginPassphrase: null,
loginPassPhraseSeedType: null,
});
Store.dispatch(
iguanaWalletPassphrase(this.state.loginPassphrase)
);
}
getLoginPassPhraseSeedType(passPhrase) {
if (!passPhrase) {
return null;
}
const passPhraseWords = passPhrase.split(" ");
if (!PassPhraseGenerator.arePassPhraseWordsValid(passPhraseWords))
return null;
if (PassPhraseGenerator.isPassPhraseValid(passPhraseWords, 256)) {
return translate('LOGIN.IGUANA_SEED');
}
if (PassPhraseGenerator.isPassPhraseValid(passPhraseWords, 160)) {
return translate('LOGIN.WAVES_SEED');
}
if (PassPhraseGenerator.isPassPhraseValid(passPhraseWords, 128)) {
return translate('LOGIN.NXT_SEED');
}
return null;
}
updateActiveLoginSection(name) {
// reset login/create form
this.setState({
activeLoginSection: name,
});
loginPassphrase: null,
loginPassPhraseSeedType: null,
seedInputVisibility: false,
bitsOption: 256,
randomSeed: PassPhraseGenerator.generatePassPhrase(256),
randomSeedConfirm: '',
isSeedConfirmError: false,
isSeedBlank: false,
displaySeedBackupModal: false,
customWalletSeed: false,
isCustomSeedWeak: false,
});
}
execWalletCreate() {
@ -156,16 +248,36 @@ class Login extends React.Component {
});
}
// TODO:
// 1) disable register btn if seed or seed conf is incorrect
// 2) display explicit custom seed validation message
handleRegisterWallet() {
if (this.state.randomSeed === this.state.randomSeedConfirm) {
const enteredSeedsMatch = this.state.randomSeed === this.state.randomSeedConfirm;
const isSeedBlank = this.isBlank(this.state.randomSeed);
// if custom seed check for string strength
// at least 1 letter in upper case
// at least 1 digit
// at least one special char
// min length 10 chars
const _customSeed = this.state.customWalletSeed ? this.state.randomSeed.match('^(?=.*[A-Z])(?=.*[^<>{}\"/|;:.,~!?@#$%^=&*\\]\\\\()\\[_+]*$)(?=.*[0-9])(?=.*[a-z]).{10,99}$') : false;
this.setState({
isCustomSeedWeak: _customSeed === null ? true : false,
isSeedConfirmError: !enteredSeedsMatch ? true : false,
isSeedBlank: isSeedBlank ? true : false,
});
if (enteredSeedsMatch && !isSeedBlank && _customSeed !== null) {
this.toggleSeedBackupModal();
} else {
this.setState({
isSeedConfirmError: true,
});
}
}
isBlank(str) {
return (!str || /^\s*$/.test(str));
}
handleKeydown(e) {
if (e.key === 'Enter') {
this.loginSeed();
@ -178,6 +290,24 @@ class Login extends React.Component {
}));
}
copyPassPhraseToClipboard() {
const passPhrase = this.state.randomSeed;
const textField = document.createElement('textarea');
textField.innerText = passPhrase;
document.body.appendChild(textField);
textField.select();
document.execCommand('copy');
textField.remove();
Store.dispatch(
triggerToaster(
translate('LOGIN.SEED_SUCCESSFULLY_COPIED'),
translate('LOGIN.SEED_COPIED'),
'success'
)
);
}
renderSwallModal() {
if (this.state.displaySeedBackupModal) {
return SwallModalRender.call(this);
@ -187,8 +317,7 @@ class Login extends React.Component {
}
render() {
if ((this.state && this.state.display) ||
!this.props.Main) {
if ((this.state && this.state.display) || !this.props.Main) {
return LoginRender.call(this);
}

216
react/src/components/login/login.render.js

@ -6,7 +6,7 @@ const LoginRender = function () {
<div>
{ this.renderSwallModal() }
<div className="page animsition vertical-align text-center fade-in">
<div className="page-content vertical-align-middle">
<div className="page-content vertical-align-middle col-xs-12 col-sm-6 col-sm-offset-3">
<div className="brand">
<img
className="brand-img"
@ -25,12 +25,16 @@ const LoginRender = function () {
<div className={ this.state.activeLoginSection === 'ieWarning' ? 'show' : 'hide' }>
<div className="panel">
<div className="panel-heading">
<h3 className="panel-title">{ translate('INDEX.UNSUPPORTED_BROWSER') }</h3>
<h3 className="panel-title">
{ translate('INDEX.UNSUPPORTED_BROWSER') }
</h3>
</div>
<div className="alert alert-danger alert-dismissible">
<button type="button">
<span>&times;</span>
<span className="sr-only">{ translate('INDEX.CLOSE') }</span>
<span className="sr-only">
{ translate('INDEX.CLOSE') }
</span>
</button>
{ translate('INDEX.IE_UNSUPPORTED') }
</div>
@ -49,54 +53,69 @@ const LoginRender = function () {
</div>
<div className={ this.state.activeLoginSection === 'login' ? 'show' : 'hide' }>
<h4 className="color-white">{translate('INDEX.WELCOME_LOGIN')}</h4>
<div className="login-form">
<div className="form-group form-material floating">
<input
type={ this.state.seedInputVisibility ? 'text' : 'password' }
className="form-control"
name="loginPassphrase"
onChange={ this.updateInput }
onKeyDown={ (event) => this.handleKeydown(event) } />
<i
className={ this.state.seedInputVisibility ? 'seed-toggle fa fa-eye-slash' : 'seed-toggle fa fa-eye' }
onClick={ this.toggleSeedInputVisibility }></i>
<label
className="floating-label"
htmlFor="inputPassword">{ translate('INDEX.WALLET_SEED') }</label>
</div>
<h4 className="color-white">
{translate('INDEX.WELCOME_LOGIN')}
</h4>
<div className="form-group form-material floating col-sm-9 horizontal-padding-0">
<input
type={ this.state.seedInputVisibility ? 'text' : 'password' }
className="form-control"
name="loginPassphrase"
onChange={ this.updateLoginPassPhraseInput }
onKeyDown={ (event) => this.handleKeydown(event) }/>
<i
className={ !this.state.seedInputVisibility ? 'seed-toggle fa fa-eye-slash' : 'seed-toggle fa fa-eye' }
onClick={ this.toggleSeedInputVisibility }></i>
<label
className="floating-label"
htmlFor="inputPassword">{ translate('INDEX.WALLET_SEED') }</label>
</div>
<div className="form-group form-material floating col-sm-3 horizontal-padding-0 margin-top-20">
{ this.state.loginPassPhraseSeedType
?
this.state.loginPassPhraseSeedType
:
<div className="placeholder-label">Seed Type</div>
}
</div>
<button
type="button"
className="btn btn-primary btn-block"
onClick={ this.loginSeed }
disabled={ !this.state.loginPassphrase
|| !this.state.loginPassphrase.length }>{ translate('INDEX.SIGN_IN') }</button>
<div className="form-group form-material floating">
<button
type="button"
className="btn btn-primary btn-block"
onClick={ this.loginSeed }
disabled={ !this.state.loginPassphrase || !this.state.loginPassphrase.length }>{ translate('INDEX.SIGN_IN') }</button>
<div className="form-group form-material floating">
<button
className="btn btn-lg btn-flat btn-block waves-effect"
id="register-btn"
onClick={ () => this.updateActiveLoginSection('signup') }>{ translate('INDEX.CREATE_WALLET') }</button>
<button
className="btn btn-lg btn-flat btn-block waves-effect hide"
id="logint-another-wallet">{ translate('INDEX.LOGIN_ANOTHER_WALLET') }</button>
<button
className="btn btn-lg btn-flat btn-block waves-effect margin-top-20"
id="register-btn"
onClick={ this.toggleActivateCoinForm }
disabled={ !this.props.Main }>
<span className="ladda-label">{ translate('ADD_COIN.ADD_ANOTHER_COIN') }</span>
</button>
</div>
className="btn btn-lg btn-flat btn-block waves-effect"
id="register-btn"
onClick={ () => this.updateActiveLoginSection('signup') }>{ translate('INDEX.CREATE_WALLET') }</button>
<button
className="btn btn-lg btn-flat btn-block waves-effect hide"
id="logint-another-wallet">{ translate('INDEX.LOGIN_ANOTHER_WALLET') }</button>
<button
className="btn btn-lg btn-flat btn-block waves-effect margin-top-20"
id="register-btn"
onClick={ this.toggleActivateCoinForm }
disabled={ !this.props.Main }>
<span className="ladda-label">
{ translate('ADD_COIN.ADD_ANOTHER_COIN') }
</span>
</button>
</div>
</div>
<div className={ this.state.activeLoginSection === 'activateCoin' ? 'show' : 'hide' }>
<h4 className="color-white">{ translate('INDEX.WELCOME_PLEASE_ADD') }</h4>
<div className="form-group form-material floating width-540 vertical-margin-30 horizontal-margin-0">
<h4 className="color-white">
{ translate('INDEX.WELCOME_PLEASE_ADD') }
</h4>
<div className="form-group form-material floating width-540 vertical-margin-30 auto-side-margin">
<button
className="btn btn-lg btn-primary btn-block ladda-button"
onClick={ this.toggleActivateCoinForm }
disabled={ !this.props.Main }>
<span className="ladda-label">{ translate('INDEX.ACTIVATE_COIN') }</span>
<span className="ladda-label">
{ translate('INDEX.ACTIVATE_COIN') }
</span>
</button>
</div>
</div>
@ -106,42 +125,89 @@ const LoginRender = function () {
<h4 className="hint color-white">
{ translate('INDEX.SELECT_SEED_TYPE') }:
</h4>
<div className="form-group form-material floating">
<div
className="radio-custom radio-default radio-inline"
onClick={ () => this.generateNewSeed(256) }>
<input
type="radio"
name="PassPhraseOptions"
checked={ this.state.bitsOption === 256 ? true : false } />
<label htmlFor="PassPhraseOptionsIguana">Iguana (256 bits)</label>
<div className="row">
<div className="col-sm-5 horizontal-padding-0">
<div className="toggle-box vertical-padding-20">
<span className="pointer">
<label className="switch">
<input
type="checkbox"
checked={ this.isCustomWalletSeed() } />
<div
className="slider"
onClick={ () => this.toggleCustomWalletSeed() }></div>
</label>
<div
className="toggle-label white"
onClick={ () => this.toggleCustomWalletSeed() }>
{ translate('LOGIN.CUSTOM_WALLET_SEED') }
</div>
</span>
</div>
</div>
<div
className="radio-custom radio-default radio-inline"
onClick={ () => this.generateNewSeed(160) }>
<input
type="radio"
name="PassPhraseOptions"
checked={ this.state.bitsOption === 160 ? true : false } />
<label htmlFor="PassPhraseOptionsWaves">Waves</label>
</div>
<div
className="radio-custom radio-default radio-inline"
onClick={ () => this.generateNewSeed(128) }>
<input
type="radio"
name="PassPhraseOptions"
checked={ this.state.bitsOption === 128 ? true : false } />
<label htmlFor="PassPhraseOptionsNXT">NXT</label>
<div className="col-sm-7 horizontal-padding-0">
{ !this.isCustomWalletSeed() &&
<div>
<div className="form-group form-material floating">
<div
className="radio-custom radio-default radio-inline"
onClick={ () => this.generateNewSeed(256) }>
<input
type="radio"
name="PassPhraseOptions"
checked={ this.state.bitsOption === 256 }/>
<label htmlFor="PassPhraseOptionsIguana">
{ translate('LOGIN.IGUANA_SEED') }
</label>
</div>
<div
className="radio-custom radio-default radio-inline"
onClick={ () => this.generateNewSeed(160) }>
<input
type="radio"
name="PassPhraseOptions"
checked={ this.state.bitsOption === 160 }/>
<label htmlFor="PassPhraseOptionsWaves">
{ translate('LOGIN.WAVES_SEED') }
</label>
</div>
<div
className="radio-custom radio-default radio-inline"
onClick={ () => this.generateNewSeed(128) }>
<input
type="radio"
name="PassPhraseOptions"
checked={ this.state.bitsOption === 128 }/>
<label htmlFor="PassPhraseOptionsNXT">
{ translate('LOGIN.NXT_SEED') }
</label>
</div>
</div>
</div>
}
</div>
</div>
<div className="form-group form-material floating">
<div className="form-group form-material floating seed-tooltip">
<textarea
className="form-control placeholder-no-fix height-100"
type="text"
id="walletseed"
value={ this.state.randomSeed }
readOnly="true"></textarea>
onChange={ (e) => this.updateWalletSeed(e) }
readOnly={ !this.isCustomWalletSeed() }
></textarea>
<button className="copy-floating-label"
htmlFor="walletseed"
onClick={ () => this.copyPassPhraseToClipboard() }>Copy</button>
<span className={ this.state.isCustomSeedWeak ? 'tooltiptext' : 'hide' }>
<strong>Weak seed!</strong><br /><br />
Your seed must contain:<br />
- at least 1 upper case letter<br />
- at least 1 digit<br />
- at least 1 special character<br />
- minimum 10 characters long
</span>
<label
className="floating-label"
htmlFor="walletseed">{ translate('INDEX.WALLET_SEED') }</label>
@ -151,9 +217,15 @@ const LoginRender = function () {
className="form-control placeholder-no-fix height-100"
type="text"
name="randomSeedConfirm"
onChange={ this.updateInput }
value={ this.state.randomSeedConfirm }
onChange={ this.updateRegisterConfirmPassPhraseInput }
id="rwalletseed"></textarea>
<span className={ this.state.isSeedConfirmError ? 'help-block' : 'hide' }>{ translate('LOGIN.ENTER_VALUE_AGAIN') }.</span>
<span className={ this.state.isSeedBlank ? 'help-block' : 'hide' }>
{ translate('LOGIN.MUST_ENTER_SEED') }.
</span>
<span className={ this.state.isSeedConfirmError ? 'help-block' : 'hide' }>
{ translate('LOGIN.ENTER_VALUE_AGAIN') }.
</span>
<label
className="floating-label"
htmlFor="rwalletseed">{ translate('INDEX.CONFIRM_SEED') }</label>

52
react/src/components/login/login.scss

@ -32,6 +32,11 @@ button {
padding-bottom: 20px;
}
.horizontal-padding-0 {
padding-left: 0;
padding-right: 0;
}
.browser-usage-container {
color: #424242;
}
@ -49,4 +54,51 @@ textarea {
&.height-100 {
height: 100px;
}
}
.placeholder-label {
color: #808080;
}
.seed-tooltip {
.tooltiptext {
width: 230px;
padding: 10px 20px;
background-color: black;
color: #fff;
text-align: left;
border-radius: 6px;
position: absolute;
z-index: 1;
top: -5px;
left: 105%;
&::after {
content: '';
position: absolute;
top: 50%;
right: 100%;
margin-top: -5px;
border-width: 5px;
border-style: solid;
border-color: transparent black transparent transparent;
}
}
}
.auto-side-margin {
margin-left: auto;
margin-right: auto;
}
button.copy-floating-label {
position: absolute;
font-weight: 500;
font-size: 11.2px;
top: -11px;
right: 0;
background-color: #757575;
border: 0;
cursor: pointer;
z-index: 1000;
}

11
react/src/translate/en.js

@ -401,7 +401,16 @@ export const _lang = {
'SIGN_IN': 'Sign In',
'WELCOME': 'Welcome',
'DISPLAY_SYNC_ONLY': 'Display sync only coins progress',
'ENTER_VALUE_AGAIN': 'Please enter the same value again'
'ENTER_VALUE_AGAIN': 'Please enter the same value again',
'CUSTOM_WALLET_SEED': 'Custom wallet seed',
'MUST_ENTER_SEED': 'You must enter a seed',
'INVALID_SEED': 'Invalid Seed',
'SEED_NOT_OF_TYPE': 'The inserted seed is not of type',
'IGUANA_SEED': 'Iguana (256 bits)',
'WAVES_SEED': 'Waves',
'NXT_SEED': 'NXT',
'SEED_COPIED': 'Seed copied',
'SEED_SUCCESSFULLY_COPIED': 'The seed was successfully copied'
},
'SIDEBAR': {
'EDEX_MOTTO': 'Most Secure, Easy and Native Decentralised Exchange',

24
react/src/util/crypto/passphrasegenerator.js

@ -80,6 +80,30 @@ export const PassPhraseGenerator = {
return this.passPhrase;
},
// checks if it's possible that the pass phrase words supplied as the first parameter
// were generated with the number of bits supplied as the second parameter
isPassPhraseValid: function (passPhraseWords, bits) {
// the required number of words based on the number of bits
// mirrors the generatePassPhrase function above
const wordsCount = bits / 32 * 3;
return passPhraseWords && passPhraseWords.length === wordsCount;
},
// checks if all pass phrase words are valid
// i.e. checks if all words are contained in ClientWordList
arePassPhraseWordsValid: function(passPhraseWords) {
if (!passPhraseWords) {
return false;
}
for (let word of passPhraseWords) {
if (this.words.indexOf(word) === -1)
return false;
}
return true;
},
reset: function() {
this.passPhrase = "";
this.seeds = 0;

Loading…
Cancel
Save