github.com/resonatecoop/id@v1.1.0-43/frontend/src/components/forms/emailUpdate.js (about)

     1  /* global fetch */
     2  
     3  const html = require('choo/html')
     4  const Component = require('choo/component')
     5  const Form = require('./generic')
     6  
     7  const isEqual = require('is-equal-shallow')
     8  const logger = require('nanologger')
     9  const log = logger('form:updateProfile')
    10  
    11  const isEmpty = require('validator/lib/isEmpty')
    12  const isEmail = require('validator/lib/isEmail')
    13  const validateFormdata = require('validate-formdata')
    14  const nanostate = require('nanostate')
    15  
    16  // EMailUpdateForm ...
    17  class EmailUpdateForm extends Component {
    18    constructor (id, state, emit) {
    19      super(id)
    20  
    21      this.emit = emit
    22      this.state = state
    23  
    24      this.local = state.components[id] = Object.create({
    25        machine: nanostate.parallel({
    26          form: nanostate('idle', {
    27            idle: { submit: 'submitted' },
    28            submitted: { valid: 'data', invalid: 'error' },
    29            data: { reset: 'idle', submit: 'submitted' },
    30            error: { reset: 'idle', submit: 'submitted', invalid: 'error' }
    31          }),
    32          request: nanostate('idle', {
    33            idle: { start: 'loading' },
    34            loading: { resolve: 'data', reject: 'error' },
    35            data: { start: 'loading' },
    36            error: { start: 'loading', stop: 'idle' }
    37          }),
    38          loader: nanostate('off', {
    39            on: { toggle: 'off' },
    40            off: { toggle: 'on' }
    41          })
    42        })
    43      })
    44  
    45      this.local.error = {}
    46  
    47      this.local.machine.on('form:reset', () => {
    48        this.validator = validateFormdata()
    49        this.local.form = this.validator.state
    50      })
    51  
    52      this.local.machine.on('request:start', () => {
    53        this.loaderTimeout = setTimeout(() => {
    54          this.local.machine.emit('loader:toggle')
    55        }, 300)
    56      })
    57  
    58      this.local.machine.on('request:reject', () => {
    59        this.emit('notify', { type: 'error', message: this.local.error.message || 'Something went wrong' })
    60  
    61        clearTimeout(this.loaderTimeout)
    62      })
    63  
    64      this.local.machine.on('request:resolve', () => {
    65        clearTimeout(this.loaderTimeout)
    66      })
    67  
    68      this.local.machine.on('form:valid', async () => {
    69        log.info('Form is valid')
    70  
    71        try {
    72          this.local.machine.emit('request:start')
    73  
    74          let response = await fetch('')
    75  
    76          const csrfToken = response.headers.get('X-CSRF-Token')
    77  
    78          response = await fetch('', {
    79            method: 'PUT',
    80            headers: {
    81              Accept: 'application/json',
    82              'X-CSRF-Token': csrfToken
    83            },
    84            redirect: 'follow',
    85            body: new URLSearchParams({
    86              email: this.local.data.email || '',
    87              password: this.local.data.password
    88            })
    89          })
    90  
    91          const status = response.status
    92          const contentType = response.headers.get('content-type')
    93  
    94          if (status >= 400 && contentType && contentType.indexOf('application/json') !== -1) {
    95            const { error } = await response.json()
    96            this.local.error.message = error
    97            this.local.machine.emit('request:reject')
    98          } else {
    99            this.local.machine.emit('request:resolve')
   100  
   101            emit('notify', {
   102              timeout: 3000,
   103              type: 'info',
   104              message: 'You have been logged out.'
   105            })
   106            setTimeout(() => {
   107              window.location.reload()
   108            }, 3000)
   109          }
   110        } catch (err) {
   111          this.local.machine.emit('request:reject')
   112          console.log(err)
   113        }
   114      })
   115  
   116      this.local.machine.on('form:invalid', () => {
   117        log.info('Form is invalid')
   118  
   119        const invalidInput = document.querySelector('.invalid')
   120  
   121        if (invalidInput) {
   122          invalidInput.focus({ preventScroll: false }) // focus to first invalid input
   123        }
   124      })
   125  
   126      this.local.machine.on('form:submit', () => {
   127        log.info('Form has been submitted')
   128  
   129        const form = this.element.querySelector('form')
   130  
   131        for (const field of form.elements) {
   132          const isRequired = field.required
   133          const name = field.name || ''
   134          const value = field.value || ''
   135  
   136          if (isRequired) {
   137            this.validator.validate(name, value)
   138          }
   139        }
   140  
   141        this.rerender()
   142  
   143        this.local.machine.emit(`form:${this.local.form.valid ? 'valid' : 'invalid'}`)
   144      })
   145  
   146      this.validator = validateFormdata()
   147      this.local.form = this.validator.state
   148    }
   149  
   150    createElement (props = {}) {
   151      this.local.data = this.local.data || props.data
   152  
   153      const values = this.local.form.values
   154  
   155      for (const [key, value] of Object.entries(this.local.data)) {
   156        values[key] = value
   157      }
   158  
   159      return html`
   160        <div class="flex flex-column flex-auto">
   161          ${this.state.cache(Form, 'account-form-update').render({
   162            id: 'account-update-email-form',
   163            method: 'POST',
   164            action: '',
   165            buttonText: 'Update my email',
   166            validate: (props) => {
   167              this.local.data[props.name] = props.value
   168              this.validator.validate(props.name, props.value)
   169              this.rerender()
   170            },
   171            form: this.local.form || {
   172              changed: false,
   173              valid: true,
   174              pristine: {},
   175              required: {},
   176              values: {},
   177              errors: {}
   178            },
   179            submit: (data) => {
   180              this.local.machine.emit('form:submit')
   181            },
   182            fields: [
   183              {
   184                type: 'email',
   185                placeholder: 'E-mail'
   186              },
   187              {
   188                type: 'password',
   189                name: 'password',
   190                id: 'password_new_email',
   191                placeholder: 'Current password'
   192              }
   193            ]
   194          })}
   195        </div>
   196      `
   197    }
   198  
   199    load () {
   200      this.validator.field('email', (data) => {
   201        if (isEmpty(data)) return new Error('Email is required')
   202        if (!isEmail(data)) return new Error('Email is invalid')
   203      })
   204      this.validator.field('password', (data) => {
   205        if (isEmpty(data)) return new Error('Password is required')
   206      })
   207    }
   208  
   209    update (props) {
   210      if (!isEqual(props.data, this.local.data)) {
   211        this.local.data = props.data
   212        return true
   213      }
   214      return false
   215    }
   216  }
   217  
   218  module.exports = EmailUpdateForm