import React from 'react'; import { toggleAddcoinModal, iguanaWalletPassphrase, iguanaActiveHandle, startInterval, getDexCoins, toggleSyncOnlyModal, getSyncOnlyForks, createNewWallet, triggerToaster, toggleLoginSettingsModal } from '../../actions/actionCreators'; import Config from '../../config'; import Store from '../../store'; 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; // TODO: remove duplicate activehandle and activecoins calls class Login extends React.Component { constructor(props) { super(props); this.state = { display: false, activeLoginSection: 'activateCoin', loginPassphrase: '', seedInputVisibility: false, loginPassPhraseSeedType: null, bitsOption: 256, randomSeed: PassPhraseGenerator.generatePassPhrase(256), randomSeedConfirm: '', isSeedConfirmError: false, isSeedBlank: false, displaySeedBackupModal: false, customWalletSeed: false, isCustomSeedWeak: false, nativeOnly: Config.iguanaLessMode, trimPassphraseTimer: null, displayLoginSettingsDropdown: false, displayLoginSettingsDropdownSection: null, }; this.toggleActivateCoinForm = this.toggleActivateCoinForm.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); this.resizeLoginTextarea = this.resizeLoginTextarea.bind(this); this.toggleLoginSettingsDropdown = this.toggleLoginSettingsDropdown.bind(this); } // the setInterval handler for 'activeCoins' _iguanaActiveCoins = null; toggleLoginSettingsDropdownSection(sectionName) { Store.dispatch(toggleLoginSettingsModal(true)); this.setState({ displayLoginSettingsDropdown: false, displayLoginSettingsDropdownSection: sectionName, }); } 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()); const _iguanaActiveHandle = setInterval(() => { Store.dispatch(getSyncOnlyForks()); }, IGUNA_ACTIVE_HANDLE_TIMEOUT); Store.dispatch( startInterval( 'syncOnly', _iguanaActiveHandle ) ); Store.dispatch(toggleSyncOnlyModal(true)); this.setState({ displayLoginSettingsDropdown: false, }); } componentDidMount() { Store.dispatch(iguanaActiveHandle(true)); } toggleSeedInputVisibility() { this.setState({ seedInputVisibility: !this.state.seedInputVisibility, }); this.resizeLoginTextarea(); } generateNewSeed(bits) { this.setState(Object.assign({}, this.state, { randomSeed: PassPhraseGenerator.generatePassPhrase(bits), bitsOption: bits, isSeedBlank: false, })); } toggleLoginSettingsDropdown() { this.setState(Object.assign({}, this.state, { displayLoginSettingsDropdown: !this.state.displayLoginSettingsDropdown, })); } componentWillReceiveProps(props) { if (props && props.Main && props.Main.isLoggedIn) { this.setState({ display: false, }); } if (props && props.Main && !props.Main.isLoggedIn) { this.setState({ display: true, }); if (!this.props.Interval.interval.activeCoins) { // only start a new 'activeCoins' interval if a previous one doesn't exist if (!this._iguanaActiveCoins) { this._iguanaActiveCoins = setInterval(() => { Store.dispatch(getDexCoins()); }, IGUNA_ACTIVE_COINS_TIMEOUT); Store.dispatch(startInterval('activeCoins', this._iguanaActiveCoins)); } } document.body.className = 'page-login layout-full page-dark'; } if (this.state.activeLoginSection !== 'signup') { if (props && props.Main && props.Main.activeCoins) { this.setState({ activeLoginSection: 'login', }); } else { this.setState({ activeLoginSection: 'activateCoin', }); } } } toggleActivateCoinForm() { Store.dispatch(toggleAddcoinModal(true, false)); } resizeLoginTextarea() { // auto-size textarea setTimeout(() => { if (this.state.seedInputVisibility) { document.querySelector('#loginPassphrase').style.height = '1px'; document.querySelector('#loginPassphrase').style.height = `${(15 + document.querySelector('#loginPassphrase').scrollHeight)}px`; } }, 100); } updateLoginPassPhraseInput(e) { // remove any empty chars from the start/end of the string const newValue = e.target.value; clearTimeout(this.state.trimPassphraseTimer); const _trimPassphraseTimer = setTimeout(() => { this.setState({ loginPassphrase: newValue ? newValue.trim() : '', // hardcoded field name loginPassPhraseSeedType: this.getLoginPassPhraseSeedType(newValue), }); }, 2000); this.resizeLoginTextarea(); this.setState({ trimPassphraseTimer: _trimPassphraseTimer, [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() { Store.dispatch( createNewWallet( this.state.randomSeedConfirm, this.props.Dashboard.activeHandle ) ); this.setState({ activeLoginSection: 'activateCoin', displaySeedBackupModal: false, isSeedConfirmError: false, }); } // TODO: disable register btn if seed or seed conf is incorrect handleRegisterWallet() { 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(); } } isBlank(str) { return (!str || /^\s*$/.test(str)); } handleKeydown(e) { this.updateLoginPassPhraseInput(e); if (e.key === 'Enter') { this.loginSeed(); } } toggleSeedBackupModal() { this.setState(Object.assign({}, this.state, { displaySeedBackupModal: !this.state.displaySeedBackupModal, })); } 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); } return null; } render() { if ((this.state && this.state.display) || !this.props.Main) { return LoginRender.call(this); } return null; } } export default Login;