[N1] Validate input in the signup dialog

Summary:
This diff bundles a number of small usability fixes to the "Create account" window. It notably:
- forces users to enter required fields before moving on to the next step
- validates email addresses and domain names

Test Plan: Tested manually by going through all the possible auth flows.

Reviewers: evan, juan, bengotow

Reviewed By: bengotow

Subscribers: bengotow

Projects: #edgehill

Differential Revision: https://phab.nylas.com/D2377
This commit is contained in:
Karim Hamidou 2016-01-15 11:27:14 -08:00
parent fc20b60b61
commit da54fa7e29
3 changed files with 120 additions and 12 deletions

View file

@ -1,4 +1,5 @@
React = require 'react'
_ = require 'underscore'
{ipcRenderer, dialog, remote} = require 'electron'
{RetinaImg} = require 'nylas-component-kit'
{EdgehillAPI, NylasAPI, APIError, Actions} = require 'nylas-exports'
@ -88,12 +89,59 @@ class AccountSettingsPage extends React.Component
settings[field] = event.target.checked
else
settings[field] = formatter(event.target.value)
setting_field = _.find(@state.provider.settings, ((e) -> return e['name'] == field))
# If the field defines an isValid method, try to validate
# the input.
if setting_field?.isValid?
if not setting_field.isValid(event.target.value)
errorFields = _.uniq(@state.errorFieldNames.concat Array(field))
@setState({errorFieldNames: errorFields})
else
errorFields = _.uniq((x for x in @state.errorFieldNames when x != field))
@setState({errorFieldNames: errorFields})
@setState({settings})
_noFormErrors: =>
allFields = @state.provider.fields.concat(@state.provider.settings || [])
fieldsOnThisPage = allFields.filter(@_fieldOnCurrentPage)
fieldNames = _.pluck(fieldsOnThisPage, 'name')
return _.intersection(fieldNames, @state.errorFieldNames).length == 0
_fieldRequired: (f) =>
return f?.required == true
_allRequiredFieldsFilled: =>
allFields = @state.provider.fields.concat(@state.provider.settings || [])
requiredFields = allFields.filter(@_fieldOnCurrentPage).filter(@_fieldRequired)
fields = _.extend(@state.fields, @state.settings)
for field in requiredFields
fieldName = field['name']
if not (fieldName of fields) or fields[fieldName] == ''
return false
return true
_onValueChanged: (event) =>
field = event.target.dataset.field
fields = @state.fields
fields[field] = event.target.value
provider_field = _.find(@state.provider.fields, ((e) -> return e['name'] == field))
# If the field defines an isValid method, try to validate
# the input.
if provider_field?.isValid?
if not provider_field.isValid(event.target.value)
errorFields = _.uniq(@state.errorFieldNames.concat [field])
@setState({errorFieldNames: errorFields})
else
errorFields = _.uniq((x for x in @state.errorFieldNames when x != field))
@setState({errorFieldNames: errorFields})
@setState({fields})
_onFieldKeyPress: (event) =>
@ -180,14 +228,23 @@ class AccountSettingsPage extends React.Component
_renderButton: =>
pages = @state.provider.pages || []
if pages.length > @state.pageNumber+1
<button className="btn btn-large btn-gradient" type="button" onClick={@_onNextButton}>Continue</button>
# We're not on the last page.
if @_noFormErrors() and @_allRequiredFieldsFilled()
<button className="btn btn-large btn-gradient" type="button" onClick={@_onNextButton}>Continue</button>
else
# Disable the "Continue" button if the fields haven't been filled correctly.
<button className="btn btn-large btn-gradient btn-disabled" type="button">Continue</button>
else if @state.provider.name isnt 'gmail'
if @state.tryingToAuthenticate
<button className="btn btn-large btn-disabled btn-add-account-spinning" type="button">
<RetinaImg name="sending-spinner.gif" width={15} height={15} mode={RetinaImg.Mode.ContentPreserve} /> Adding account&hellip;
</button>
else
<button className="btn btn-large btn-gradient btn-add-account" type="button" onClick={@_onSubmit}>Add account</button>
if @_noFormErrors() and @_allRequiredFieldsFilled()
<button className="btn btn-large btn-gradient btn-add-account" type="button" onClick={@_onSubmit}>Add account</button>
else
# Disable the "Add Account" button if the fields haven't been filled correctly.
<button className="btn btn-large btn-gradient btn-add-account btn-disabled" type="button">Add account</button>
_onNextButton: (event) =>
@setState(pageNumber: @state.pageNumber + 1)

View file

@ -1,9 +1,15 @@
RegExpUtils = require('nylas-exports').RegExpUtils
validEmail = (address) ->
return RegExpUtils.emailRegex().test(address)
validDomain = (domain) ->
return RegExpUtils.domainRegex().test(domain)
Providers = [
{
name: 'gmail'
displayName: 'Gmail'
displayName: 'Gmail or Google Apps'
icon: 'ic-settings-account-gmail.png'
header_icon: 'setup-icon-provider-gmail.png'
color: '#e99999'
@ -14,17 +20,23 @@ Providers = [
icon: 'ic-settings-account-eas.png'
header_icon: 'setup-icon-provider-exchange.png'
color: '#1ea2a3'
pages: ['Set up your Exchange account', 'Advanced settings']
fields: [
{
name: 'name'
type: 'text'
placeholder: 'Ashton Letterman'
label: 'Name'
required: true
page: 0
}, {
name: 'email'
type: 'text'
type: 'email'
placeholder: 'you@example.com'
label: 'Email'
isValid: validEmail
required: true
page: 0
}
]
settings: [
@ -33,16 +45,21 @@ Providers = [
type: 'text'
placeholder: 'MYCORP\\bob (if known)'
label: 'Username (optional)'
page: 1
}, {
name: 'password'
type: 'password'
placeholder: 'Password'
label: 'Password'
required: true
page: 1
}, {
name: 'eas_server_host'
type: 'text'
placeholder: 'mail.company.com'
label: 'Exchange server (optional)'
name: 'eas_server_host'
type: 'text'
placeholder: 'mail.company.com'
label: 'Exchange server (optional)'
isValid: validDomain
page: 1
}
]
}, {
@ -57,11 +74,16 @@ Providers = [
type: 'text'
placeholder: 'Ashton Letterman'
label: 'Name'
required: true
page: 0
}, {
name: 'email'
type: 'text'
type: 'email'
placeholder: 'you@icloud.com'
label: 'Email'
isValid: validEmail
required: true
page: 0
}
]
settings: [{
@ -69,6 +91,8 @@ Providers = [
type: 'password'
placeholder: 'Password'
label: 'Password'
required: true
page: 0
}]
}, {
name: 'outlook'
@ -82,11 +106,16 @@ Providers = [
type: 'text'
placeholder: 'Ashton Letterman'
label: 'Name'
required: true
page: 0
}, {
name: 'email'
type: 'text'
type: 'email'
placeholder: 'you@hotmail.com'
label: 'Email'
isValid: validEmail
required: true
page: 0
}
]
settings: [{
@ -94,6 +123,8 @@ Providers = [
type: 'password'
placeholder: 'Password'
label: 'Password'
required: true
page: 0
}]
}, {
name: 'yahoo'
@ -107,11 +138,16 @@ Providers = [
type: 'text'
placeholder: 'Ashton Letterman'
label: 'Name'
required: true
page: 0
}, {
name: 'email'
type: 'text'
type: 'email'
placeholder: 'you@yahoo.com'
label: 'Email'
isValid: validEmail
required: true
page: 0
}
]
settings: [{
@ -119,6 +155,7 @@ Providers = [
type: 'password'
placeholder: 'Password'
label: 'Password'
required: true
}]
}, {
name: 'imap'
@ -133,12 +170,15 @@ Providers = [
placeholder: 'Ashton Letterman'
label: 'Name'
page: 0
required: true
}, {
name: 'email'
type: 'text'
type: 'email'
placeholder: 'you@example.com'
label: 'Email'
isValid: validEmail
page: 0
required: true
}
]
settings: [
@ -148,6 +188,8 @@ Providers = [
placeholder: 'imap.domain.com'
label: 'IMAP Server'
page: 1
required: true
isValid: validDomain
}, {
name: 'imap_port'
type: 'text'
@ -170,18 +212,22 @@ Providers = [
placeholder: 'Username'
label: 'Username'
page: 1
required: true
}, {
name: 'imap_password'
type: 'password'
placeholder: 'Password'
label: 'Password'
page: 1
required: true
}, {
name: 'smtp_host'
type: 'text'
placeholder: 'smtp.domain.com'
label: 'SMTP Server'
page: 2
required: true
isValid: validDomain
}, {
name: 'smtp_port'
type: 'text'
@ -204,12 +250,14 @@ Providers = [
placeholder: 'Username'
label: 'Username'
page: 2
required: true
}, {
name: 'smtp_password'
type: 'password'
placeholder: 'Password'
label: 'Password'
page: 2
required: true
}
]
# }, {

View file

@ -13,6 +13,9 @@ RegExpUtils =
# https://en.wikipedia.org/wiki/Email_address#Local_part
emailRegex: -> new RegExp(/([a-z.A-Z0-9!#$%&'*+\-/=?^_`{|}~;:]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,63})/g)
# http://stackoverflow.com/a/16463966
domainRegex: -> new RegExp(/^(?!:\/\/)([a-zA-Z0-9]+\.)?[a-zA-Z0-9][a-zA-Z0-9-]+\.[a-zA-Z]{2,11}?$/i)
# https://regex101.com/r/zG7aW4/3
imageTagRegex: -> /<img\s+[^>]*src="([^"]*)"[^>]*>/g