Skip to content
This repository has been archived by the owner on Apr 15, 2019. It is now read-only.

Commit

Permalink
Merge branch 'development' into 486-toaster
Browse files Browse the repository at this point in the history
  • Loading branch information
slaweet authored Jul 27, 2017
2 parents 26c59f3 + be43911 commit 397bdf7
Show file tree
Hide file tree
Showing 27 changed files with 1,103 additions and 14 deletions.
1 change: 1 addition & 0 deletions .storybook/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ function loadStories() {
require('../src/components/dialog/stories');
require('../src/components/formattedNumber/stories');
require('../src/components/toaster/stories');
require('../src/components/send/stories');
}

configure(loadStories, module);
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"postcss-cssnext": "=2.11.0",
"prop-types": "=15.5.10",
"react": "=15.6.x",
"react-animate-on-change": "^1.0.0",
"react-circular-progressbar": "=0.1.5",
"react-dom": "=15.6.x",
"react-redux": "=5.0.3",
Expand Down
8 changes: 7 additions & 1 deletion src/components/header/headerElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import logo from '../../assets/images/LISK-nano.png';
import styles from './header.css';
import VerifyMessage from '../signVerify/verifyMessage';
import SignMessage from '../signVerify/signMessage';
import Send from '../send';

const HeaderElement = props => (
<header className={styles.wrapper}>
Expand Down Expand Up @@ -37,7 +38,12 @@ const HeaderElement = props => (
/>
</IconMenu>
<Button className={styles.button} raised>logout</Button>
<Button className={styles.button} raised primary>send</Button>
<Button className={styles.button}
raised primary
onClick={() => props.setActiveDialog({
title: 'Send',
childComponent: Send,
})}>Send</Button>
</header>
);

Expand Down
11 changes: 2 additions & 9 deletions src/components/liskAmount/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import React from 'react';
import BigNumber from 'bignumber.js';
import { fromRawLsk } from '../../utils/lsk';
import FormattedNumber from '../formattedNumber';

/**
*
* @param {*} num - it is a number that we want to normalize it
* @return {string} - normalized version of input number
*/
const normalize = value => new BigNumber(value || 0).dividedBy(new BigNumber(10).pow(8)).toFixed();

const LiskValue = props => (<FormattedNumber val={parseFloat(normalize(props.val))} />);
const LiskValue = props => (<FormattedNumber val={parseFloat(fromRawLsk(props.val))} />);

export default LiskValue;

3 changes: 3 additions & 0 deletions src/components/login/login.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
margin-top: 8px;
padding-bottom: 24px !important;
}
.newAccount {
margin-right: 8px;
}
2 changes: 2 additions & 0 deletions src/components/login/loginForm.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import { dialogDisplayed } from '../../actions/dialog';
import LoginFormComponent from './loginFormComponent';
import { accountUpdated } from '../../actions/account';
import { activePeerSet } from '../../actions/peers';
Expand All @@ -15,6 +16,7 @@ const mapStateToProps = state => ({
const mapDispatchToProps = dispatch => ({
onAccountUpdated: data => dispatch(accountUpdated(data)),
activePeerSet: network => dispatch(activePeerSet(network)),
setActiveDialog: data => dispatch(dialogDisplayed(data)),
});

const LoginFormConnected = connect(
Expand Down
19 changes: 15 additions & 4 deletions src/components/login/loginFormComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import Checkbox from 'react-toolbox/lib/checkbox';
import { getAccount } from '../../utils/api/account';
import { getDelegate } from '../../utils/api/delegate';
import networksRaw from './networks';
import Passphrase from '../passphrase';
import styles from './login.css';

if (global._bitcore) delete global._bitcore;

Expand Down Expand Up @@ -78,7 +80,7 @@ class LoginFormComponent extends React.Component {
this.setState({ [name]: value });
}

onLoginSubmission() {
onLoginSubmission(passphrase) {
const network = Object.assign({}, networksRaw[this.state.network]);
if (this.state.network === 2) {
network.address = this.state.address;
Expand All @@ -89,7 +91,7 @@ class LoginFormComponent extends React.Component {
setTimeout(() => {
// get account info
const { onAccountUpdated } = this.props;
onAccountUpdated({ passphrase: this.state.passphrase });
onAccountUpdated({ passphrase });
const accountInfo = this.props.account;

// redirect to main/transactions
Expand Down Expand Up @@ -143,8 +145,17 @@ class LoginFormComponent extends React.Component {
/>
<footer className={ `${grid.row} ${grid['center-xs']}` }>
<div className={grid['col-xs-12']}>
<Button label='NEW ACCOUNT' flat primary />
<Button label='LOGIN' primary raised onClick={this.onLoginSubmission.bind(this)}
<Button label='NEW ACCOUNT' flat primary
className={styles.newAccount}
onClick={() => this.props.setActiveDialog({
title: 'New Account',
childComponent: Passphrase,
childComponentProps: {
onPassGenerated: this.onLoginSubmission.bind(this),
},
})} />
<Button label='LOGIN' primary raised
onClick={this.onLoginSubmission.bind(this, this.state.passphrase)}
disabled={(this.state.network === 2 && this.state.addressValidity !== '') ||
this.state.passphraseValidity !== ''} />
</div>
Expand Down
24 changes: 24 additions & 0 deletions src/components/passphrase/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { connect } from 'react-redux';
import Passphrase from './passphrase';
import { accountUpdated } from '../../actions/account';
import { activePeerSet } from '../../actions/peers';

/**
* Using react-redux connect to pass state and dispatch to LoginForm
*/
const mapStateToProps = state => ({
account: state.account,
peers: state.peers,
});

const mapDispatchToProps = dispatch => ({
onAccountUpdated: data => dispatch(accountUpdated(data)),
activePeerSet: network => dispatch(activePeerSet(network)),
});

const PassphraseConnected = connect(
mapStateToProps,
mapDispatchToProps,
)(Passphrase);

export default PassphraseConnected;
37 changes: 37 additions & 0 deletions src/components/passphrase/passphrase.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.byte {
display: inline-block;
text-align: center;
font-size: 140%;
margin: 5px;
font-family: monospace;
transition: all ease 300ms;
}

.missing {
padding: 0 5px 0;
font-weight: bold;
color: #0288d1;
}
.stable {
transform: scale(1);
display: inline-block;
transition: all ease 300ms;
}
.bouncing {
transform: scale(1.2);
}
hr {
display: none;
}
.templateItem {
min-height: 130px;
}
:global .box {
box-shadow: none !important;
}
.cancel {
margin-left: 8px;
}
.approve {
margin-right: 8px;
}
80 changes: 80 additions & 0 deletions src/components/passphrase/passphrase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from 'react';
import Button from 'react-toolbox/lib/button';
import Input from 'react-toolbox/lib/input';
import grid from 'flexboxgrid/dist/flexboxgrid.css';
import styles from './passphrase.css';
import InfoParagraph from '../infoParagraph';
import PassphraseGenerator from './passphraseGenerator';
import PassphraseConfirmator from './passphraseConfirmator';
import steps from './steps';

class Passphrase extends React.Component {
constructor() {
super();
this.state = {
steps: steps(this),
currentStep: 'info',
answer: '',
};
}

changeHandler(name, value) {
this.setState({ [name]: value });
}

render() {
const templates = {};

// Step 1: Information/introduction
templates.info = <InfoParagraph className={styles.noHr}>
Please click Next, then move around your mouse randomly to generate a random passphrase.
<br />
<br />
Note: After registration completes, your passphrase will be
required for logging in to your account.
<br />
This passphrase is not recoverable and if you lose it, you will
lose access to your account forever. Please keep it safe!
</InfoParagraph>;

// step 2: Generator, binds mouse events
templates.generate = <PassphraseGenerator
changeHandler={this.changeHandler.bind(this)} />;

// step 3: Confirmation, Asks for a random word to make sure the user has copied the passphrase
templates.show = <Input type='text' multiline label='Passphrase'
value={this.state.passphrase} />;

// step 4: Confirmation, Asks for a random word to make sure the user has copied the passphrase
templates.confirm = <PassphraseConfirmator
passphrase={this.state.passphrase}
answer={this.state.answer}
updateAnswer={this.changeHandler.bind(this, 'answer')} />;

return (
<div>
<section className={`${styles.templateItem} ${grid['middle-xs']}`}>
<div className={grid['col-xs-12']}>
<div className='box'>
{ templates[this.state.currentStep] }
</div>
</div>
</section>
<section className={`${grid.row} ${grid['between-xs']}`}>
<Button label={this.state.steps[this.state.currentStep].cancelButton.title}
className={`${styles.cancel} cancel-button`}
onClick={this.state.steps[this.state.currentStep].cancelButton.onClick.bind(this)} />

<Button label={this.state.steps[this.state.currentStep].confirmButton.title}
primary={true} raised={true}
className={styles.approve}
disabled={(this.state.currentStep === 'generate' && !this.state.passphrase) ||
(this.state.currentStep === 'confirm' && !this.state.answer)}
onClick={this.state.steps[this.state.currentStep].confirmButton.onClick.bind(this)}/>
</section>
</div>
);
}
}

export default Passphrase;
39 changes: 39 additions & 0 deletions src/components/passphrase/passphrase.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import chai, { expect } from 'chai';
import sinonChai from 'sinon-chai';
import { mount } from 'enzyme';
import Passphrase from './passphrase';
import InfoParagraph from '../infoParagraph';
import PassphraseGenerator from './passphraseGenerator';
import PassphraseConfirmator from './passphraseConfirmator';

chai.use(sinonChai);

describe('ForgedBlocks', () => {
let wrapper;

beforeEach(() => {
wrapper = mount(<Passphrase />);
});

it('should render 2 buttons', () => {
expect(wrapper.find('button')).to.have.lengthOf(2);
});

it('should intially render InfoParagraph', () => {
expect(wrapper.find(InfoParagraph)).to.have.lengthOf(1);
});

it('should render PassphraseGenerator component if step is equal info', () => {
wrapper.setState({ currentStep: 'generate' });
expect(wrapper.find(PassphraseGenerator)).to.have.lengthOf(1);
});

it('should render PassphraseConfirmator component if step is equal confirm', () => {
wrapper.setState({
currentStep: 'confirm',
passphrase: 'survey stereo pool fortune oblige slight gravity goddess mistake sentence anchor pool',
});
expect(wrapper.find(PassphraseConfirmator)).to.have.lengthOf(1);
});
});
62 changes: 62 additions & 0 deletions src/components/passphrase/passphraseConfirmator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import Input from 'react-toolbox/lib/input';
import grid from 'flexboxgrid/dist/flexboxgrid.css';
import styles from './passphrase.css';

class PassphraseConfirmator extends React.Component {
constructor() {
super();
this.state = {
passphraseParts: [],
};
}

componentDidMount() {
this.props.updateAnswer(false);
this.state = {
passphraseParts: this.hideRandomWord.call(this),
};
}

hideRandomWord(rand = Math.random()) {
const words = this.props.passphrase.trim().split(/\s+/);
const index = Math.floor(rand * words.length);

this.setState({
passphraseParts: this.props.passphrase.split(` ${words[index]} `),
missing: words[index],
answer: '',
});
}

changeHandler(value) {
this.props.updateAnswer(value === this.state.missing);
}

// eslint-disable-next-line
focus({ nativeEvent }) {
nativeEvent.target.focus();
}

render() {
return (
<div className={`${grid.row} ${grid['start-xs']}`}>
<div className={grid['col-xs-12']}>
<p>
<span>{this.state.passphraseParts[0]}</span>
<span className={styles.missing}>-----</span>
<span>{this.state.passphraseParts[1]}</span>
</p>
</div>
<div className={grid['col-xs-12']}>
<Input type='text' label='Enter the missing word'
autoFocus
onBlur={this.focus.bind(this)}
onChange={this.changeHandler.bind(this)} />
</div>
</div>
);
}
}

export default PassphraseConfirmator;
Loading

0 comments on commit 397bdf7

Please sign in to comment.