<commit_before>import React from 'react'
import { ShellScreen } from '@blockstack/ui'
import Yup from 'yup'

import debounce from 'lodash.debounce'
import log4js from 'log4js'

const logger = log4js.getLogger('onboarding/Username.js')

const defaultSponsoredName = '.test-personal.id'

/**
 * Gets the status of submitted username
 */
const getUsernameStatus = async (
  username,
  sponsoredName = defaultSponsoredName
) => {
  if (!username) {
    return null
  }
  const api = 'https://test-registrar.blockstack.org/v1/names/'
  // const url = `https://core.blockstack.org/v1/names/${username.toLowerCase()}${sponsoredName}`
  const url = `${api}${username.toLowerCase()}${sponsoredName}`
  const res = await fetch(url)
  const user = await res.json()
  logger.debug('got response', user)
  return user.status
}

const errorMessage = 'A username is required.'

class UsernameView extends React.Component {
  state = {
    status: null,
    username: ''
  }

  validate = async values => {
    console.log('validating', values)

    const noValues = !values.username || values.username === ''
    let errors = {}

    if (noValues) {
      errors.username = errorMessage
      throw errors
    }

    if (values.username !== this.state.username && !noValues) {
      const status = await getUsernameStatus(values.username)

      this.setState({
        status,
        username: values.username
      })
    }

    const isRegistered = !noValues && this.state.status !== 'available'
    if (isRegistered) {
      errors.username = 'Username unavailable'
    } else {
      errors = {}
    }

    if (Object.keys(errors).length) {
      throw errors
    }
    return null
  }

  renderButtonLabel = ({ status }) => {
    switch (status) {
      case 'available':
        return 'Register Username'
      default:
        return 'Check Availability'
    }
  }

  processRegistration = (username, next) => next(username)

  constructor(props) {
    super(props)

    this.validate = debounce(this.validate, 300)
  }

  render() {
    const { updateValue, next, loading, ...rest } = this.props

    const props = {
      title: {
        children: 'Register a username',
        variant: 'h2'
      },
      content: {
        grow: 0,
        form: {
          validate: v => this.validate(v),
          initialValues: { username: '' },
          onSubmit: values => {
            if (
              this.state.status === 'available' &&
              this.state.username === values.username
            ) {
              updateValue('username', values.username)
              this.processRegistration(values.username, next)
            }
          },
          fields: [<commit_msg>Onboarding username validation and error handling.<commit_after>import React from 'react'
import { ShellScreen } from '@blockstack/ui'
import Yup from 'yup'

import debounce from 'lodash.debounce'
import log4js from 'log4js'

const logger = log4js.getLogger('onboarding/Username.js')

const defaultSponsoredName = '.test-personal.id'

/**
 * Gets the status of submitted username
 */
const getUsernameStatus = async (
  username,
  sponsoredName = defaultSponsoredName
) => {
  if (!username) {
    return null
  }
  const api = 'https://test-registrar.blockstack.org/v1/names/'
  // const url = `https://core.blockstack.org/v1/names/${username.toLowerCase()}${sponsoredName}`
  const url = `${api}${username.toLowerCase()}${sponsoredName}`
  const res = await fetch(url)
  const user = await res.json()
  logger.debug('got response', user)
  return user.status
}

const errorMessage = 'A username is required.'

class UsernameView extends React.Component {
  state = {
    status: null,
    username: ''
  }

  validate = async values => {
    console.log('validating', values)

    const noValues = !values.username || values.username === ''
    let errors = {}

    if (noValues) {
      errors.username = errorMessage
      throw errors
    }

    if (values.username !== this.state.username && !noValues) {

      const invalidChars = values.username.match(/\W+/g)

      if (invalidChars) {
        errors.username = 'Invalid username (a-zA-Z0-9_)'
        throw errors
      } else {
        const status = await getUsernameStatus(values.username)

        this.setState({
          status,
          username: values.username
        })
      }

    }

    const isRegistered = !noValues && this.state.status !== 'available'
    if (isRegistered) {
      errors.username = 'Username unavailable'
      throw errors
    } else {
      errors = {}
    }

    if (Object.keys(errors).length) {
      throw errors
    }
    return null
  }

  renderButtonLabel = ({ status }) => {
    switch (status) {
      case 'available':
        return 'Register Username'
      default:
        return 'Check Availability'
    }
  }

  processRegistration = (username, next) => next(username)

  constructor(props) {
    super(props)

    // this.validate = debounce(this.validate, 300)
  }

  render() {
    const { updateValue, next, loading, ...rest } = this.props

    const props = {
      title: {
        children: 'Register a username',
        variant: 'h2'
      },
      content: {
        grow: 0,
        form: {
          validate: v => this.validate(v),
          initialValues: { username: '' },
          onSubmit: values => {
            if (
              this.state.status === 'available' &&
              this.state.username === values.username
            ) {
              updateValue('username', values.username)
              this.processRegistration(values.username, next)
            }
          },
          fields: [
{'fraction_non_alphanumeric': 0.12648994840775662, 'fraction_numerical': 0.003913894324853229, 'mean_word_length': 2.378605769230769, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3935344', 'n_tokens_mistral': 1651, 'n_tokens_neox': 1609, 'n_words': 387}