diff options
| author | Andrej Mihajlov <and@codeispoetry.ru> | 2017-02-13 13:40:42 +0000 |
|---|---|---|
| committer | Andrej Mihajlov <and@codeispoetry.ru> | 2017-02-13 13:40:42 +0000 |
| commit | 307243bc50f19f572bb6fd2399efe093d21415f8 (patch) | |
| tree | c9143cda355097fa5f693e82ad70cdcae8f1f167 /app/components | |
| parent | 6fe70786242de8aae0d592f658ace0624a369685 (diff) | |
| download | mullvadvpn-307243bc50f19f572bb6fd2399efe093d21415f8.tar.xz mullvadvpn-307243bc50f19f572bb6fd2399efe093d21415f8.zip | |
- Add ES6 enums
- Add react-if
- Add conditional rendering in login component
- Implement basic login logic
Diffstat (limited to 'app/components')
| -rw-r--r-- | app/components/Login.css | 10 | ||||
| -rw-r--r-- | app/components/Login.js | 97 | ||||
| -rw-r--r-- | app/components/Tray.js | 19 |
3 files changed, 107 insertions, 19 deletions
diff --git a/app/components/Login.css b/app/components/Login.css index db38b20097..a10163c2af 100644 --- a/app/components/Login.css +++ b/app/components/Login.css @@ -10,6 +10,16 @@ flex: 0 0 auto; } +.login-footer--invisible { + visibility: hidden; +} + +.login-form__status-icon { + text-align: center; + margin-bottom: 44px; + height: 36px; +} + .login-footer__prompt { font-family: "Open Sans"; font-size: 15px; diff --git a/app/components/Login.js b/app/components/Login.js index d503200a21..a9aaec81c5 100644 --- a/app/components/Login.js +++ b/app/components/Login.js @@ -1,30 +1,53 @@ import { shell } from 'electron'; import React, { Component, PropTypes } from 'react'; +import { If, Then, Else } from 'react-if'; import Layout from './Layout'; -import constants from '../constants'; +import { createAccountURL, LoginState } from '../constants'; export default class Login extends Component { static propTypes = { - onLogin: PropTypes.func.isRequired + user: PropTypes.object.isRequired, + onLogin: PropTypes.func.isRequired, + onChange: PropTypes.func.isRequired, + onFirstChangeAfterFailure: PropTypes.func.isRequired }; - + constructor(props) { super(props); - this.state = { account: '' }; + this.state = { notifyOnFirstChangeAfterFailure: false }; + } + + componentWillReceiveProps(nextProps) { + const prev = this.props.user || {}; + const next = nextProps.user || {}; + + if(prev.status !== next.status && next.status === LoginState.failed) { + this.setState({ notifyOnFirstChangeAfterFailure: true }); + } } handleLogin() { - this.props.onLogin(this.props.backend, this.state.account); + const { account } = this.props.user; + if(account.length > 0) { + this.props.onLogin(account); + } } handleCreateAccount() { - shell.openExternal(constants.createAccountURL); + shell.openExternal(createAccountURL); } handleInputChange(e) { const val = e.target.value.replace(/[^0-9]/g, ''); - this.setState({ account: val }); + + // notify delegate on first change after login failure + if(this.state.notifyOnFirstChangeAfterFailure) { + this.setState({ notifyOnFirstChangeAfterFailure: false }); + this.props.onFirstChangeAfterFailure(); + } + + this.props.onChange(val); } handleInputKeyUp(e) { @@ -43,25 +66,71 @@ export default class Login extends Component { return val.replace(/([0-9]{4})/g, '$1 ').trim(); } + formTitle(s) { + switch(s) { + case LoginState.connecting: return "Logging in..."; + case LoginState.failed: return "Login failed"; + case LoginState.ok: return "Logged in"; + default: return "Login"; + } + } + + formSubtitle(s, e) { + switch(s) { + case LoginState.failed: return e.message; + case LoginState.connecting: return 'Checking account number'; + default: return 'Enter your account number'; + } + } + render() { + console.log(this.props.user); + const { account, status, error } = this.props.user; + const title = this.formTitle(status); + const subtitle = this.formSubtitle(status, error); + const isConnecting = status === LoginState.connecting; + const isFailed = status === LoginState.failed; + const inputClass = ["login-form__input-field", isFailed ? "login-form__input-field--error" : ""].join(' '); + const footerClass = ["login-footer", isConnecting ? "login-footer--invisible" : ""].join(' '); + return ( <Layout> <div className="login"> <div className="login-form"> <div> - <div className="login-form__title">Login</div> - <div className="login-form__subtitle">Enter your account number</div> + + { /* show spinner when connecting */ } + <If condition={ isConnecting }> + <Then> + <div className="login-form__status-icon"> + <img src="./assets/images/icon-spinner.svg" alt="" /> + </div> + </Then> + </If> + + { /* show error icon when failed */ } + <If condition={ isFailed }> + <Then> + <div className="login-form__status-icon"> + <img src="./assets/images/icon-fail.svg" alt="" /> + </div> + </Then> + </If> + + <div className="login-form__title">{ title }</div> + <div className="login-form__subtitle">{ subtitle }</div> <div className="login-form__input-wrap"> - <input className="login-form__input-field" + <input className={ inputClass } type="text" placeholder="0000 0000 0000" - onChange={::this.handleInputChange} - onKeyUp={::this.handleInputKeyUp} - value={this.formattedAccount(this.state.account)} /> + onChange={ ::this.handleInputChange } + onKeyUp={ ::this.handleInputKeyUp } + value={ this.formattedAccount(account) } + disabled={ isConnecting } /> </div> </div> </div> - <div className="login-footer"> + <div className={footerClass}> <div className="login-footer__prompt">Don't have an account number?</div> <button className="login-footer__button" onClick={::this.handleCreateAccount}>Create account</button> </div> diff --git a/app/components/Tray.js b/app/components/Tray.js index 745fbc20d1..11075cab36 100644 --- a/app/components/Tray.js +++ b/app/components/Tray.js @@ -1,5 +1,6 @@ import React, { Component, PropTypes } from 'react'; import { TrayMenu, TrayItem } from '../lib/components/TrayMenu'; +import { shell } from 'electron'; export default class Tray extends Component { @@ -11,16 +12,24 @@ export default class Tray extends Component { this.props.login({ username: '', loggedIn: false }); this.props.history.push('/'); } + + openPrivacyPolicy() { + shell.openExternal('https://mullvad.net/#privacy'); + } + + openHomepage() { + shell.openExternal('https://mullvad.net'); + } render() { const loggedIn = this.props.user && this.props.user.loggedIn; return ( - <TrayMenu tray={this.props.handle}> - <TrayItem label="Log out" click={::this.logout} visible={loggedIn} /> - <TrayItem type="separator" visible={loggedIn} /> - <TrayItem label="Privacy Policy" /> - <TrayItem label="Visit homepage" /> + <TrayMenu tray={ this.props.handle }> + <TrayItem label="Log out" click={ ::this.logout } visible={ loggedIn } /> + <TrayItem type="separator" visible={ loggedIn } /> + <TrayItem label="Privacy Policy" click={ ::this.openPrivacyPolicy } /> + <TrayItem label="Visit homepage" click={ ::this.openHomepage } /> </TrayMenu> ); } |
