React = require 'react' ipc = require 'ipc' {RetinaImg} = require 'nylas-component-kit' {EdgehillAPI, NylasAPI, APIError} = require 'nylas-exports' OnboardingActions = require './onboarding-actions' NylasApiEnvironmentStore = require './nylas-api-environment-store' Providers = require './account-types' class AccountSettingsPage extends React.Component @displayName: "AccountSettingsPage" constructor: (@props) -> @state = provider: @props.pageData.provider settings: {} fields: {} pageNumber: 0 errorFieldNames: [] errorMessage: null show_advanced: false @props.pageData.provider.settings.forEach (field) => if field.default? @state.settings[field.name] = field.default if @state.provider.name is 'gmail' poll_attempt_id = 0 done = false # polling with capped exponential backoff delay = 1000 tries = 0 poll = (id,initial_delay) => _retry = => tries++ @_pollForGmailAccount((account) -> if account? done = true OnboardingActions.accountJSONReceived(account) else if tries < 10 and id is poll_attempt_id setTimeout(_retry, delay) delay *= 1.5 # exponential backoff ) setTimeout(_retry,initial_delay) ipc.on('browser-window-focus', -> if not done # hack to deactivate this listener when done poll_attempt_id++ poll(poll_attempt_id,0) ) poll(poll_attempt_id,2000) componentDidMount: -> componentWillUnmount: -> render: ->
{@_renderTitle()}
{@_renderErrorMessage()}
{@_renderFields()} {@_renderSettings()} {@_renderButton()}
_onSettingsChanged: (event) => field = event.target.dataset.field format = event.target.dataset.format int_formatter = (a) -> i = parseInt(a) if isNaN(i) then "" else i formatter = if format is 'integer' then int_formatter else (a) -> a settings = @state.settings if event.target.type is 'checkbox' settings[field] = event.target.checked else settings[field] = formatter(event.target.value) @setState({settings}) _onValueChanged: (event) => field = event.target.dataset.field fields = @state.fields fields[field] = event.target.value @setState({fields}) _onFieldKeyPress: (event) => if event.key in ['Enter', 'Return'] pages = @state.provider.pages || [] if pages.length > @state.pageNumber+1 @_onNextButton() else @_onSubmit() _renderTitle: => if @state.provider.name is 'gmail'

Sign in to {@state.provider.displayName} in your browser.

else if @state.provider.pages?.length > 0

{@state.provider.pages[@state.pageNumber]}

else

Sign in to {@state.provider.displayName}

_renderErrorMessage: => if @state.errorMessage
{@state.errorMessage ? ""}
_fieldOnCurrentPage: (field) => !@state.provider.pages || field.page is @state.pageNumber _renderFields: => @state.provider.fields?.filter(@_fieldOnCurrentPage) .map (field, idx) => errclass = if field.name in @state.errorFieldNames then "error " else "" _renderSettings: => @state.provider.settings?.filter(@_fieldOnCurrentPage) .map (field, idx) => if field.type is 'checkbox' else errclass = if field.name in @state.errorFieldNames then "error " else "" _renderButton: => pages = @state.provider.pages || [] if pages.length > @state.pageNumber+1 else if @state.provider.name isnt 'gmail' if @state.tryingToAuthenticate else _onNextButton: (event) => @setState(pageNumber: @state.pageNumber + 1) @_resize() _onSubmit: (event) => return if @state.tryingToAuthenticate data = settings: {} for own k,v of @state.fields when v isnt '' data[k] = v for own k,v of @state.settings when v isnt '' data.settings[k] = v data.provider = @state.provider.name # handle special case for exchange/outlook/hotmail username field if data.provider in ['exchange','outlook','hotmail'] and not data.settings.username?.trim().length data.settings.username = data.email @setState(tryingToAuthenticate: true) # Send the form data directly to Nylas to get code # If this succeeds, send the received code to N1 server to register the account # Otherwise process the error message from the server and highlight UI as needed NylasAPI.makeRequest path: "/auth?client_id=#{NylasAPI.AppID}" method: 'POST' body: data returnsModel: false timeout: 30000 auth: user: '' pass: '' sendImmediately: true .then (json) => EdgehillAPI.request path: "/connect/nylas" method: "POST" timeout: 30000 body: json success: (json) => OnboardingActions.accountJSONReceived(json) error: @_onNetworkError .catch(@_onNetworkError) _onNetworkError: (err) => errorMessage = err.message pageNumber = @state.pageNumber errorFieldNames = err.body?.missing_fields || err.body?.missing_settings if errorFieldNames {pageNumber, errorMessage} = @_stateForMissingFieldNames(errorFieldNames) if err.statusCode is -123 # timeout errorMessage = "Request timed out. Please try again." @setState pageNumber: pageNumber errorMessage: errorMessage errorFieldNames: errorFieldNames || [] tryingToAuthenticate: false @_resize() _stateForMissingFieldNames: (fieldNames) -> fieldLabels = [] fields = [].concat(@state.provider.settings, @state.provider.fields) pageNumbers = [@state.pageNumber] for fieldName in fieldNames for s in fields when s.name is fieldName fieldLabels.push(s.label.toLowerCase()) if s.page isnt undefined pageNumbers.push(s.page) pageNumber = Math.min.apply(null, pageNumbers) errorMessage = @_messageForFieldLabels(fieldLabels) {pageNumber, errorMessage} _messageForFieldLabels: (labels) -> if labels.length > 2 return "Please fix the highlighted fields." else if labels.length is 2 return "Please provide your #{labels[0]} and #{labels[1]}." else return "Please provide your #{labels[0]}." _pollForGmailAccount: (callback) => EdgehillAPI.request path: "/oauth/google/token?key="+@state.provider.clientKey method: "GET" success: (json) => callback(json) error: (err) => callback() _resize: => setTimeout( => @props.onResize?() ,10) _fireMoveToPrevPage: => if @state.pageNumber > 0 @setState(pageNumber: @state.pageNumber-1) @_resize() else OnboardingActions.moveToPreviousPage() module.exports = AccountSettingsPage