UNPKG

15.7 kBJSXView Raw
1import React from 'react'
2import ReactDOM from 'react-dom'
3import { Link } from 'react-router'
4import {ABCError} from 'airbitz-core-js'
5
6var strings = require('./abcui-strings')
7
8var AbcUiFormView = require('./abcui-formview.jsx')
9var LoginWithAirbitz = require('./abcui-loginwithairbitz.jsx')
10var classNames = require('classnames')
11
12var modal = require('./abcui-modal.jsx')
13var BootstrapButton = modal.BootstrapButton
14var BootstrapModal = modal.BootstrapModal
15var BootstrapInput = modal.BootstrapInput
16
17var PasswordRequirementsInput = require('./abcui-password.jsx')
18
19var context = window.parent.abcContext
20
21var AbcUserList = React.createClass({
22 getInitialState () {
23 return { showInput: false }
24 },
25 render () {
26 var block = null
27 var userList = context ? context.usernameList().sort() : []
28 var toggleInput = null
29 if (this.props.allowInput) {
30 toggleInput = (
31 <span className="input-group-btn">
32 <button type="button" onClick={this.toggleInput} className="btn btn-primary">X</button>
33 </span>)
34 }
35 if (this.props.allowInput && (userList.length === 0 || this.state.showInput)) {
36 block = (
37 <div className="input-group">
38 <input autoFocus ref="username" type="text" placeholder="username" className="form-control" />
39 <span className="input-group-btn">
40 <button type="button" onClick={this.toggleInput} className="btn btn-primary">X</button>
41 </span>
42 </div>
43 )
44 } else {
45 var selectElement = (
46 <select ref="username"
47 className="form-control"
48 onChange={this.handleSelection}
49 defaultValue={this.props.username}>
50 {
51 userList.map(function (username) {
52 return (<option value={username} key={username}>{username}</option>)
53 })
54 }
55 </select>
56 )
57 if (this.props.allowInput) {
58 return (
59 <div className="input-group">
60 {selectElement}
61 {toggleInput}
62 </div>
63 )
64 } else {
65 return selectElement
66 }
67 }
68 return (block)
69 },
70 toggleInput () {
71 this.setState({'showInput': !this.state.showInput})
72 if (this.state.showInput) {
73 this.setState({username: ''})
74 this.refs.username.focus()
75 }
76 },
77 handleSelection () {
78 this.props.onUserChange(this.refs.username.value)
79 },
80 getValue () {
81 return this.refs.username.value
82 }
83})
84
85var AbcPasswordLoginForm = React.createClass({
86 render () {
87 return (
88 <AbcUiFormView ref="form">
89 <div className="row">
90 <div className="col-sm-12">
91 <div className="form-group">
92 <AbcUserList
93 ref="username"
94 allowInput
95 username={this.props.username}
96 onUserChange={this.props.onUserChange} />
97 </div>
98 </div>
99 </div>
100 <div className="row">
101 <div className="col-sm-12">
102 <div className="form-group">
103 <input ref="password" type="password" onKeyPress={this.handlePasswordKeyPress} placeholder={strings.password_text} className="form-control" />
104 </div>
105 </div>
106 </div>
107 <div className="row">
108 <div className="col-sm-12 text-center">
109 <div className="form-group">
110 <BootstrapButton ref="signin" onClick={this.handleSubmit}>Sign In</BootstrapButton>
111 </div>
112 </div>
113 </div>
114 <div className="row">
115 <div className="col-sm-12 text-center">
116 <div className="form-group">
117 <Link className="btn btn-link" to={'/recovery'}>Forgot Password</Link>
118 </div>
119 </div>
120 </div>
121 </AbcUiFormView>
122 )
123 },
124 onClose () {
125 },
126 handlePasswordKeyPress (e) {
127 if (e.key === 'Enter') {
128 this.handleSubmit()
129 }
130 },
131 handleSubmit () {
132 var that = this
133 this.refs.signin.setLoading(true)
134 this.refs.form.setState({'error': null})
135 context.loginWithPassword(this.refs.username.getValue(), this.refs.password.value, null, null, function (err, result) {
136 if (err) {
137 that.refs.form.setState({'error': ABCError(err, strings.invalid_password_text).message})
138 } else {
139 that.props.onSuccess(result)
140 }
141 that.refs.signin.setLoading(false)
142 })
143 return false
144 }
145})
146
147var AbcPinLoginForm = React.createClass({
148 render () {
149 return (
150 <AbcUiFormView ref="form">
151 <div className="row">
152 <div className="col-sm-12 text-center">
153 <div className="form-group center-block" style={{'width': '240px'}}>
154 <AbcUserList ref="username"
155 allowInput={false}
156 username={this.props.username}
157 onUserChange={this.props.onUserChange} />
158 </div>
159 </div>
160 <div className="col-sm-12 text-center">
161 <div className="form-group center-block" style={{'width': '100px'}}>
162 <input ref="pin" type="password" placeholder={strings.pin_text} onKeyPress={this.handlePinKeyPress} className="form-control" maxLength="4" />
163 </div>
164 </div>
165 </div>
166 <div className="row">
167 <div className="col-sm-12 text-center">
168 <div className="form-group center-block" style={{'width': '240px'}}>
169 <BootstrapButton ref="signin" onClick={this.handleSubmit}>{strings.sign_in_text}</BootstrapButton>
170 </div>
171 </div>
172 </div>
173 <div className="row">
174 <div className="col-sm-12 text-center">
175 <div className="form-group center-block" style={{'width': '240px'}}>
176 <button type="button" onClick={this.handleExit} className="btn-link">{strings.exit_pin_login_text}</button>
177 </div>
178 </div>
179 </div>
180 </AbcUiFormView>
181 )
182 },
183 onClose () {
184 },
185 handleExit () {
186 this.props.onExit()
187 return false
188 },
189 handlePinKeyPress (e) {
190 if (e.key === 'Enter') {
191 this.handleSubmit()
192 }
193 },
194 handleSubmit () {
195 var that = this
196 this.refs.signin.setLoading(true)
197 context.loginWithPIN(this.refs.username.getValue(), this.refs.pin.value, function (err, result) {
198 if (err) {
199 that.refs.form.setState({'error': ABCError(err, 'Failed to login with PIN.').message})
200 } else {
201 that.props.onSuccess(result)
202 }
203 that.refs.signin.setLoading(false)
204 })
205 return false
206 }
207})
208
209var LoginView = React.createClass({
210 getInitialState () {
211 var doRegistration = context.usernameList().sort().length === 0
212 return {
213 forcePasswordLogin: false,
214 showRegistration: doRegistration
215 }
216 },
217 statics: {
218 currentUser () {
219 return window.localStorage.getItem('airbitz.current_user')
220 },
221 updateCurrentUser (username) {
222 window.localStorage.setItem('airbitz.current_user', username)
223 }
224 },
225 render () {
226 var block = null
227 var currentUser = LoginView.currentUser()
228 var showPinLogin = context && currentUser && context.pinExists(currentUser)
229 if (this.state.forcePasswordLogin) {
230 showPinLogin = false
231 }
232 if (this.state.showRegistration) {
233 block = (
234 <div>
235 <RegistrationForm />
236 <button type="button" onClick={this.handleSignIn} className="btn-link">{strings.sign_in_text}</button>
237 </div>
238 )
239 } else if (showPinLogin) {
240 block = (
241 <div>
242 <AbcPinLoginForm ref="pinPasswordForm"
243 username={currentUser}
244 onSuccess={this.handleSuccess}
245 onError={this.handleError}
246 onUserChange={this.handleUserChange}
247 onExit={this.handlePinExit} />
248 <button type="button" onClick={this.handleSignUp} className="btn-link">{strings.sign_up_text}</button>
249 </div>
250 )
251 } else {
252 block = (
253 <div>
254 <AbcPasswordLoginForm ref="pinPasswordForm"
255 username={currentUser}
256 onSuccess={this.handleSuccess}
257 onError={this.handleError}
258 onUserChange={this.handleUserChange} />
259 <button type="button" onClick={this.handleSignUp} className="btn-link">{strings.sign_up_text}</button>
260 </div>
261 )
262 }
263 return (
264 <BootstrapModal
265 ref="loginModal"
266 key="loginModal"
267 cancel="Cancel"
268 title="Airbitz Edge Login"
269 onClose={this.onClose}>
270 <LoginWithAirbitz onLogin={this.handleSuccess} ref="loginWithAirbitz" />
271 {block}
272 </BootstrapModal>
273 )
274 },
275 handlePinExit () {
276 this.setState({'forcePasswordLogin': true})
277 },
278 handleUserChange (newUsername) {
279 LoginView.updateCurrentUser(newUsername)
280 this.setState({'forcePasswordLogin': false})
281 },
282 handleSuccess (account) {
283 this.refs.loginWithAirbitz.cancelRequest()
284 this.setState({'forcePasswordLogin': false})
285 LoginView.updateCurrentUser(account.username)
286 if (window.parent.loginCallback) {
287 this.refs.loginModal.close()
288 window.parent.loginCallback(null, account)
289 }
290 },
291 handleSignIn () {
292 this.setState({showRegistration: false})
293 },
294 handleSignUp () {
295 this.setState({showRegistration: true})
296 },
297 onClose () {
298 this.refs.loginWithAirbitz.cancelRequest()
299 if (this.refs.pinPasswordForm) {
300 this.refs.pinPasswordForm.onClose()
301 }
302 if (window.parent.exitCallback) {
303 window.parent.exitCallback()
304 }
305 }
306})
307
308var RegistrationForm = React.createClass({
309 getInitialState () {
310 return {
311 showSuccess: false,
312 account: null,
313 usernameError: false
314 }
315 },
316 render () {
317 var usernameClass = classNames({
318 'form-group': true,
319 'has-error': this.state.usernameError
320 })
321 var regForm = (
322 <AbcUiFormView ref="form">
323 <div className="row">
324 <div className="col-sm-12">
325 <div className={usernameClass}>
326 <BootstrapInput type="text" ref="username" onKeyPress={this.handleKeypressUsername} placeholder="Choose a Username" className="form-control" onBlur={this.blur} onFocus={this.focus} />
327 </div>
328 </div>
329 <div className="col-sm-12">
330 <div className="form-group">
331 <PasswordRequirementsInput ref="password" onKeyPress={this.handleKeypressPassword} placeholder="Choose a Password" className="form-control" />
332 </div>
333 </div>
334 <div className="col-sm-12">
335 <div className="form-group">
336 <PasswordRequirementsInput ref="password_repeat" onKeyPress={this.handleKeypressPassword2} placeholder="Repeat Password" className="form-control" />
337 </div>
338 </div>
339
340 <div className="col-sm-12">
341 <div className="form-group">
342 <div className="input-group">
343 <input type="password" ref="pin" onKeyPress={this.handleKeypressPin} maxLength="4" placeholder="Choose a 4 Digit PIN" className="form-control" />
344 </div>
345 </div>
346 </div>
347 <div className="col-sm-12">
348 <div className="form-group">
349 <span className="input-group-btn">
350 <BootstrapButton ref="register" onClick={this.handleSubmit}>Register</BootstrapButton>
351 </span>
352 </div>
353 </div>
354 </div>
355 </AbcUiFormView>
356 )
357
358 var successMessage = (
359 <div>
360 <BootstrapModal ref="regModal" title={strings.account_created_text} onClose={this.onClose}>
361 {String.format(strings.account_created_message, window.parent.abcuiContext.vendorName)}
362 <br /><br />
363 {String.format(strings.account_created_zero_knowledge, window.parent.abcuiContext.vendorName)}
364 <br /><br />
365 {String.format(strings.account_created_write_it_down, window.parent.abcuiContext.vendorName)}
366 <br /><br />
367 <span className="input-group-btn">
368 <BootstrapButton onClick={this.onSuccessSetupRecovery}>{strings.setup_recovery_text}</BootstrapButton>
369 </span>
370 <span className="input-group-btn">
371 <BootstrapButton onClick={this.onSuccessClose}>{strings.later_button_text}</BootstrapButton>
372 </span>
373 </BootstrapModal>
374 </div>
375 )
376
377 if (this.state.showSuccess) {
378 return successMessage
379 } else {
380 return regForm
381 }
382 },
383 focus () {
384 this.refs.username.setState({error: null, loading: null})
385 },
386 blur () {
387 var that = this
388 var username = that.refs.username.value()
389 if (username) {
390 that.refs.username.setState({error: null, loading: 'Checking availability...'})
391 context.usernameAvailable(username, function (err) {
392 if (err) {
393 that.setState({usernameError: true})
394 that.refs.username.setState({error: strings.username_already_taken, loading: null})
395 } else {
396 that.setState({usernameError: false})
397 that.refs.username.setState({error: null, loading: null})
398 }
399 })
400 } else {
401 that.refs.username.setState({error: null})
402 }
403 },
404 handleKeypressUsername (e) {
405 if (e.key === 'Enter') {
406 this.refs.password.setFocus()
407 }
408 },
409 handleKeypressPassword (e) {
410 if (e.key === 'Enter') {
411 this.refs.password_repeat.setFocus()
412 }
413 },
414 handleKeypressPassword2 (e) {
415 if (e.key === 'Enter') {
416 ReactDOM.findDOMNode(this.refs.pin).focus()
417 }
418 },
419 handleKeypressPin (e) {
420 if (e.key === 'Enter') {
421 this.handleSubmit()
422 }
423 },
424 handleSubmit () {
425 var that = this
426 if (this.refs.password.value() !== this.refs.password_repeat.value()) {
427 that.refs.form.setState({ 'error': 'Passwords do not match' })
428 return false
429 }
430 var checkPasswdResults = context.checkPasswordRules(this.refs.password.value())
431 if (!checkPasswdResults.passed) {
432 that.refs.form.setState({ 'error': 'Insufficient Password' })
433 return false
434 }
435 if (this.refs.pin.value.length !== 4) {
436 that.refs.form.setState({ 'error': 'PIN Must be 4 digits long' })
437 return false
438 }
439 var onlyNumbers = /^\d+$/.test(that.refs.pin.value)
440 if (!onlyNumbers) {
441 that.refs.form.setState({ 'error': 'PIN must only have numbers' })
442 return false
443 }
444
445 this.refs.register.setLoading(true)
446 var username = this.refs.username.value()
447 context.createAccount(username, this.refs.password.value(), this.refs.pin.value, function (err, result) {
448 that.refs.register.setLoading(false)
449 if (err) {
450 that.refs.form.setState({'error': ABCError(err, 'Unable to register at this time.').message})
451 } else {
452 var account = result
453 LoginView.updateCurrentUser(account.username)
454 that.setState({account: account})
455 that.setState({showSuccess: true})
456 }
457 })
458 return false
459 },
460 onLogin (account) {
461 LoginView.updateCurrentUser(account.username)
462 // Need to Add UI to ask for a PIN
463 // account.pinSetup(that.refs.pin.value, function(err, result) {
464 if (window.parent.loginCallback) {
465 window.parent.loginCallback(null, account)
466 }
467 this.refs.regModal.close()
468 this.refs.register.setLoading(false)
469 // })
470 },
471 onClose () {
472 if (window.parent.exitCallback) {
473 window.parent.exitCallback()
474 }
475 },
476 onSuccessClose () {
477 if (window.parent.loginCallback) {
478 window.parent.loginCallback(null, this.state.account)
479 }
480 },
481 onSuccessSetupRecovery () {
482 if (window.parent.loginCallback) {
483 window.parent.loginCallback(null, this.state.account, {setupRecovery: true})
484 }
485 }
486})
487
488module.exports = LoginView