ducky/web

Paddy 2015-07-07 Parent:e6da0d35a533 Child:21f80f56cda9

19:6b7037b4cbe7 Go to Latest

ducky/web/src/pages/register.jsx

Update to hosted URL, use oauth-refresh in profiles. When syncing the profiles, use our oauth-refresh sync helper, so it won't fail because of an expired OAuth token. Also, update our URL to use the nginx-fronted URL.

History
paddy@0 1 import app from 'ampersand-app'
paddy@0 2 import React from 'react/addons'
paddy@0 3 import localLinks from 'local-links'
paddy@0 4 import LaddaButton from 'react-ladda'
paddy@0 5 import LaddaCSS from '../../node_modules/ladda/dist/ladda.min.css'
paddy@0 6 import HeroUnit from '../components/hero'
paddy@2 7 import ValidationError from '../components/validation-error'
paddy@0 8 import onboardingStyles from '../styles/onboarding.scss'
paddy@2 9 import flashStyles from '../styles/_flashes.scss'
paddy@2 10 import debounce from 'lodash.debounce'
paddy@0 11
paddy@0 12 export default React.createClass({
paddy@0 13 displayName: 'RegisterPage',
paddy@0 14 mixins: [React.addons.LinkedStateMixin],
paddy@0 15
paddy@0 16 getInitialState () {
paddy@0 17 return {
paddy@0 18 email: null,
paddy@0 19 emailConfirmation: null,
paddy@0 20 passphrase: null,
paddy@0 21 passphraseConfirmation: null,
paddy@0 22 active: false,
paddy@4 23 clientErrors: [],
paddy@4 24 serverErrors: [],
paddy@0 25 }
paddy@0 26 },
paddy@0 27
paddy@2 28 emailValidationOutputs: {
paddy@2 29 'missing': 'We need to know how to contact you. We promise not to share it.',
paddy@2 30 'overflow': 'Hm, that’s a bit long. Do you have a shorter email address?',
paddy@2 31 'invalid_format': 'That doesn’t look like an email address… Double check it?',
paddy@7 32 'conflict': 'Hm, that email already has an account. Did you forget your password?',
paddy@2 33 },
paddy@2 34
paddy@2 35 emailConfirmationValidationOutputs: {
paddy@2 36 'invalid_value': 'Oops! Those emails don’t match. Maybe double-check them?',
paddy@2 37 },
paddy@2 38
paddy@2 39 passphraseValidationOutputs: {
paddy@2 40 'insufficient': 'A longer passphrase would be better.',
paddy@2 41 'missing': 'Looks like you forgot to enter a passphrase. You need one!',
paddy@2 42 'overflow': 'We can’t store that long a passphrase. Try a shorter one.',
paddy@2 43 },
paddy@2 44
paddy@2 45 passphraseConfirmationValidationOutputs: {
paddy@2 46 'invalid_value': 'Oops! Those passphrases don’t match. Maybe double-check them?',
paddy@2 47 },
paddy@2 48
paddy@6 49 catchAllValidationOutputs: {
paddy@7 50 'act_of_god': 'Hm, something went wrong. Try again? Or let support know.',
paddy@7 51 'invalid_form': 'Uh oh, things went really wrong. Let support know you saw the dreaded invalid_format error!',
paddy@7 52 'conflict': 'Something went wrong, but trying again will probably fix it.',
paddy@7 53 'access_denied': 'Oops, something’s gone awry. Let support know your client isn’t working.',
paddy@6 54 },
paddy@6 55
paddy@2 56 debouncedValidateForm (event) {
paddy@2 57 this._validateForm(event)
paddy@2 58 },
paddy@2 59
paddy@2 60 _validateForm (event) {
paddy@2 61 const fields = {
paddy@2 62 email: this.state.email,
paddy@2 63 emailConfirmation: this.state.emailConfirmation,
paddy@2 64 passphrase: this.state.passphrase,
paddy@2 65 passphraseConfirmation: this.state.passphraseConfirmation,
paddy@2 66 }
paddy@2 67 if (event != null) {
paddy@2 68 if (event.target.id == 'emailInput') {
paddy@2 69 fields.email = event.target.value
paddy@2 70 }
paddy@2 71 else if (event.target.id == 'emailConfirmationInput') {
paddy@2 72 fields.emailConfirmation = event.target.value
paddy@2 73 }
paddy@2 74 else if (event.target.id == 'passphraseInput') {
paddy@2 75 fields.passphrase = event.target.value
paddy@2 76 }
paddy@2 77 else if (event.target.id == 'passphraseConfirmationInput') {
paddy@2 78 fields.passphraseConfirmation = event.target.value
paddy@2 79 }
paddy@2 80 }
paddy@2 81 const errors = this.validate(fields, event == null)
paddy@7 82 this.setState({clientErrors: errors, serverErrors: []})
paddy@2 83 return errors.length <= 0
paddy@2 84 },
paddy@2 85
paddy@2 86 validateForm (event) {
paddy@2 87 event.persist()
paddy@2 88 this.debouncedValidateForm(event)
paddy@2 89 },
paddy@2 90
paddy@2 91 validate (fields, all) {
paddy@2 92 const errors = []
paddy@2 93 if (fields.email != null) {
paddy@2 94 if (fields.email.length == 0) {
paddy@2 95 errors.push({'error': 'missing', 'field': '/email'})
paddy@2 96 } else if (fields.email.length > 64) {
paddy@2 97 errors.push({'error': 'overflow', 'field': '/email'})
paddy@2 98 } else if (!fields.email.match(/.+@.+\..+/)) {
paddy@2 99 errors.push({'error': 'invalid_format', 'field': '/email'})
paddy@2 100 }
paddy@2 101 } else if (all) {
paddy@2 102 errors.push({'error': 'missing', 'field': '/email'})
paddy@2 103 }
paddy@2 104 if (fields.emailConfirmation != null || all) {
paddy@2 105 if (fields.emailConfirmation != fields.email && (fields.email || fields.emailConfirmation)) {
paddy@2 106 errors.push({'error': 'invalid_value', 'field': '/email_confirmation'})
paddy@2 107 }
paddy@2 108 }
paddy@2 109 if (fields.passphrase != null) {
paddy@2 110 if (fields.passphrase.length == 0) {
paddy@2 111 errors.push({'error': 'missing', 'field': '/passphrase'})
paddy@2 112 } else if (fields.passphrase.length < 6) {
paddy@2 113 errors.push({'error': 'insufficient', 'field': '/passphrase'})
paddy@2 114 } else if (fields.passphrase.length > 64) {
paddy@2 115 errors.push({'error': 'overflow', 'field': '/passphrase'})
paddy@2 116 }
paddy@2 117 } else if (all) {
paddy@2 118 errors.push({'error': 'missing', 'field': '/passphrase'})
paddy@2 119 }
paddy@2 120 if (fields.passphraseConfirmation != null || all) {
paddy@2 121 if (fields.passphraseConfirmation != fields.passphrase && (fields.passphrase || fields.passphraseConfirmation)) {
paddy@2 122 errors.push({'error': 'invalid_value', 'field': '/passphrase_confirmation'})
paddy@2 123 }
paddy@2 124 }
paddy@2 125 return errors
paddy@2 126 },
paddy@2 127
paddy@0 128 componentDidMount () {
paddy@0 129 app.profiles.on('request', (moc, xhr, options) => {
paddy@0 130 this.setState({active: true})
paddy@0 131 })
paddy@0 132 app.profiles.on('error', (moc, xhr, options) => {
paddy@4 133 let state = {active: false}
paddy@7 134 let resp = {}
paddy@7 135 if (xhr && xhr.response) {
paddy@7 136 resp = JSON.parse(xhr.response)
paddy@7 137 }
paddy@7 138 if (resp.errors && resp.errors.length) {
paddy@7 139 state.serverErrors = resp.errors
paddy@4 140 } else {
paddy@4 141 state.serverErrors = [{'error': 'act_of_god'}]
paddy@4 142 }
paddy@4 143 this.setState(state)
paddy@0 144 })
paddy@0 145 app.profiles.on('sync', (moc, xhr, options) => {
paddy@0 146 app.me.login(this.state.email, this.state.passphrase)
paddy@0 147 })
paddy@0 148 app.me.on('sync', (moc, xhr, options) => {
paddy@0 149 this.setState({active: false})
paddy@2 150 app.router.navigate('/register/payment')
paddy@0 151 })
paddy@4 152 app.me.on('error', (moc, xhr, options) => {
paddy@4 153 let state = {active: false}
paddy@7 154 let resp = {}
paddy@7 155 if (xhr && xhr.response) {
paddy@7 156 resp = JSON.parse(xhr.response)
paddy@7 157 }
paddy@7 158 console.log(resp)
paddy@7 159 if (resp.errors && resp.errors.length) {
paddy@7 160 state.serverErrors = resp.errors
paddy@7 161 } else if (resp.error && resp.error == 'invalid_client') {
paddy@7 162 state.serverErrors = [{'error': 'access_denied'}]
paddy@4 163 } else {
paddy@4 164 state.serverErrors = [{'error': 'act_of_god'}]
paddy@4 165 }
paddy@4 166 this.setState(state)
paddy@4 167 })
paddy@0 168 },
paddy@0 169
paddy@2 170 componentWillMount () {
paddy@2 171 this.debouncedValidateForm = debounce(this.debouncedValidateForm, 900)
paddy@2 172 },
paddy@2 173
paddy@0 174 register (e) {
paddy@0 175 e.preventDefault()
paddy@2 176 const success = this._validateForm(null)
paddy@2 177 if (!success) {
paddy@2 178 return
paddy@2 179 }
paddy@0 180 app.profiles.register(this.state.email, this.state.passphrase)
paddy@0 181 },
paddy@0 182
paddy@0 183 onBackClick (event) {
paddy@0 184 event.preventDefault()
paddy@0 185 window.history.back()
paddy@0 186 },
paddy@0 187
paddy@0 188 render () {
paddy@0 189 return (
paddy@0 190 <div className='container'>
paddy@0 191 <HeroUnit title='Create an Account'>We’d like to get to know you better.</HeroUnit>
paddy@0 192 <article className='onboarding register'>
paddy@0 193 <form onSubmit={this.register}>
paddy@0 194 <div>
paddy@2 195 <label htmlFor='emailInput'>Email</label>
paddy@2 196 <input id='emailInput' type='email' placeholder='Ours is quack@useducky.com' valueLink={this.linkState('email')} disabled={this.state.active} onInput={this.validateForm} />
paddy@4 197 <ValidationError errors={this.state.clientErrors.concat(this.state.serverErrors)} field='/email' outputs={this.emailValidationOutputs} />
paddy@0 198
paddy@2 199 <label htmlFor='emailConfirmationInput'>Verify Email</label>
paddy@2 200 <input id='emailConfirmationInput' type='email' placeholder='Typos are the absolute worst.' valueLink={this.linkState('emailConfirmation')} disabled={this.state.active} onInput={this.validateForm} />
paddy@4 201 <ValidationError errors={this.state.clientErrors.concat(this.state.serverErrors)} field='/email_confirmation' outputs={this.emailConfirmationValidationOutputs} />
paddy@0 202
paddy@2 203 <label htmlFor='passphraseInput'>Passphrase</label>
paddy@2 204 <input id='passphraseInput' type='password' placeholder='We use a sentence. Try it!' valueLink={this.linkState('passphrase')} disabled={this.state.active} onInput={this.validateForm} />
paddy@4 205 <ValidationError errors={this.state.clientErrors.concat(this.state.serverErrors)} field='/passphrase' outputs={this.passphraseValidationOutputs} />
paddy@0 206
paddy@2 207 <label htmlFor='passphraseConfirmationInput'>Verify Passphrase</label>
paddy@2 208 <input id='passphraseConfirmationInput' type='password' placeholder='Just to make sure you know it.' valueLink={this.linkState('passphraseConfirmation')} disabled={this.state.active} onInput={this.validateForm} />
paddy@4 209 <ValidationError errors={this.state.clientErrors.concat(this.state.serverErrors)} field='/passphrase_confirmation' outputs={this.passphraseConfirmationValidationOutputs} />
paddy@6 210
paddy@6 211 <ValidationError errors={this.state.clientErrors.concat(this.state.serverErrors)} notFields={['/email', '/email_confirmation', '/passphrase', '/passphrase_confirmation']} notHeaders={[]} notParams={[]} outputs={this.catchAllValidationOutputs} />
paddy@0 212 </div>
paddy@0 213 <div className='actionbuttons'>
paddy@0 214 <button onClick={this.onBackClick} disabled={this.state.active} type='button' className='ladda-button'>Back</button>
paddy@0 215 <LaddaButton style='expand-right' active={this.state.active}>
paddy@4 216 <button type='submit' className='primary' disabled={this.state.active || this.state.clientErrors.length}>Register</button>
paddy@0 217 </LaddaButton>
paddy@0 218 </div>
paddy@0 219 </form>
paddy@0 220 </article>
paddy@0 221 </div>
paddy@0 222 )
paddy@0 223 }
paddy@0 224 })