<commit_before>// @flow
import React from 'react'
import { wallet } from 'neon-js'
import { progressValues } from 'spunky'
import classNames from 'classnames'
import { get } from 'lodash-es'

import Button from '../../components/Button'
import StyledReactSelect from '../../components/Inputs/StyledReactSelect/StyledReactSelect'
import Label from '../../components/Inputs/Label'
import LoginIcon from '../../assets/icons/login.svg'
import ConfirmIcon from '../../assets/icons/confirm.svg'
import RefreshIcon from '../../assets/icons/refresh.svg'
import styles from '../Home/Home.scss'
import { getPublicKeys } from '../../ledger/ledgerNanoS'

import { MESSAGES } from '../../ledger/neonLedger'

const LEDGER_CONNECTION_STAGES = {
  NOT_CONNECTED: 1,
  OPEN_APP: 2,
  CONNECTED: 3
}

const { LOADED, FAILED } = progressValues

const { NOT_CONNECTED, OPEN_APP, CONNECTED } = LEDGER_CONNECTION_STAGES

type LedgerConnectionStage = $Values<typeof LEDGER_CONNECTION_STAGES>

type LedgerPublicKey = { account: number, key: string }

type Props = {
  progress: string,
  publicKey: LedgerPublicKey,
  login: Function,
  connect: Function,
  error: ?string
}

type SelectOption = {
  label: string,
  value: string
}

type State = {
  ledgerStage: LedgerConnectionStage,
  isLoading: boolean,
  addressOption: SelectOption | null,
  publicKeys: Array<LedgerPublicKey>,
  loadingPublicKeys: boolean,
  error: string | null
}

const POLL_FREQUENCY_MS = 1000

const FETCH_ADDITIONAL_KEYS_ERROR = 'Error fetching additional public keys.'

export default class LoginLedgerNanoS extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props)
    this.state = {
      ...this.computeStateFromProps(props),
      addressOption: null,
      publicKeys: [],
      loadingPublicKeys: true,
      error: null
    }
  }

  intervalId: IntervalID

  static defaultProps = {
    publicKeys: []
  }

  componentDidMount() {
    this.intervalId = setInterval(this.props.connect, POLL_FREQUENCY_MS)
  }

  componentWillReceiveProps(nextProps: Props) {
    const { progress, error } = this.props

    if (nextProps.publicKey && !this.state.addressOption) {
      this.setState({
        publicKeys: [nextProps.publicKey],
        loadingPublicKeys: false,
        addressOption: {
          value: nextProps.publicKey.key,
          label: this.unencodedHexToAddress(nextProps.publicKey.key)
        }
      })
    }

    if (progress !== nextProps.progress || error !== nextProps.error) {
      this.setState(this.computeStateFromProps(nextProps))
    }
  }

  computeStateFromProps = (props: Props) => {
    if (props.progress === LOADED) {
      if (this.intervalId) {
        clearInterval(this.intervalId)
      }
      return {
        ledgerStage: CONNECTED,
        isLoading: false
      }
    }
    if (props.progress === FAILED && props.error) {
      return {
        isLoading: true,
        ledgerStage:
          props.error === MESSAGES.APP_CLOSED ? OPEN_APP : NOT_CONNECTED
      }
    }
    return {
      isLoading: true,
      ledgerStage: NOT_CONNECTED
    }
  }

  componentWillUnmount() {
    if (this.intervalId) {
      clearInterval(this.intervalId)
    }
  }

  render() {
    const options = this.createOptionsFromKeys()
    const { loadingPublicKeys, publicKeys } = this.state

    return (
      <div id="loginLedgerNanoS" className={styles.flexContainer}>
        <form>
          {this.renderStatus()}
          <Label
            label="public address:"
            renderAdditionalContent={this.renderAdditionalLabelContent}
          />
          <StyledReactSelect
            value={this.state.addressOption}
            isDisabled={publicKeys.length === 1}
            onChange={addressOption => this.setState({ addressOption })}
            options={options}
            onMenuScrollToBottom={this.fetchAdditionalKeys}
            isSearchable
            isLoading={loadingPublicKeys}
          />
          <Button
            id="loginButton"
            primary
            type="submit"
            className={styles.loginButtonMargin}
            renderIcon={LoginIcon}
            disabled={!this.canLogin()}
            onClick={this.handleLogin}
            shouldCenterButtonLabelText
          >
            Login
          </Button>
        </form>
      </div>
    )
  }

  fetchAdditionalKeys = async () => {
    const { publicKeys } = this.state

    this.setState({ loadingPublicKeys: true, error: null })

    const lastAccountLoaded = publicKeys[publicKeys.length - 1].account

    const nextBatchOfKeys = await getPublicKeys(lastAccountLoaded).catch(() => {
      console.error(
        'An error occurred getting additional public keys from ledger'
      )
      this.setState({
        error: FETCH_ADDITIONAL_KEYS_ERROR,
        loadingPublicKeys: false
      })
    })

    if (nextBatchOfKeys) {
      this.setState(state => ({
        publicKeys: [...state.publicKeys, ...nextBatchOfKeys],
        loadingPublicKeys: false
      }))
    }
  }

  renderAdditionalLabelContent = () => {
    const { loadingPublicKeys, error } = this.state
    if (!loadingPublicKeys && !error) {<commit_msg>fixes bug causing dupes in dropdown<commit_after>// @flow
import React from 'react'
import { wallet } from 'neon-js'
import { progressValues } from 'spunky'
import classNames from 'classnames'
import { get } from 'lodash-es'

import Button from '../../components/Button'
import StyledReactSelect from '../../components/Inputs/StyledReactSelect/StyledReactSelect'
import Label from '../../components/Inputs/Label'
import LoginIcon from '../../assets/icons/login.svg'
import ConfirmIcon from '../../assets/icons/confirm.svg'
import RefreshIcon from '../../assets/icons/refresh.svg'
import styles from '../Home/Home.scss'
import { getPublicKeys } from '../../ledger/ledgerNanoS'

import { MESSAGES } from '../../ledger/neonLedger'

const LEDGER_CONNECTION_STAGES = {
  NOT_CONNECTED: 1,
  OPEN_APP: 2,
  CONNECTED: 3
}

const { LOADED, FAILED } = progressValues

const { NOT_CONNECTED, OPEN_APP, CONNECTED } = LEDGER_CONNECTION_STAGES

type LedgerConnectionStage = $Values<typeof LEDGER_CONNECTION_STAGES>

type LedgerPublicKey = { account: number, key: string }

type Props = {
  progress: string,
  publicKey: LedgerPublicKey,
  login: Function,
  connect: Function,
  error: ?string
}

type SelectOption = {
  label: string,
  value: string
}

type State = {
  ledgerStage: LedgerConnectionStage,
  isLoading: boolean,
  addressOption: SelectOption | null,
  publicKeys: Array<LedgerPublicKey>,
  loadingPublicKeys: boolean,
  error: string | null
}

const POLL_FREQUENCY_MS = 1000

const FETCH_ADDITIONAL_KEYS_ERROR = 'Error fetching additional public keys.'

export default class LoginLedgerNanoS extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props)
    this.state = {
      ...this.computeStateFromProps(props),
      addressOption: null,
      publicKeys: [],
      loadingPublicKeys: true,
      error: null
    }
  }

  intervalId: IntervalID

  static defaultProps = {
    publicKeys: []
  }

  componentDidMount() {
    this.intervalId = setInterval(this.props.connect, POLL_FREQUENCY_MS)
  }

  componentWillReceiveProps(nextProps: Props) {
    const { progress, error } = this.props

    if (nextProps.publicKey && !this.state.addressOption) {
      this.setState({
        publicKeys: [nextProps.publicKey],
        loadingPublicKeys: false,
        addressOption: {
          value: nextProps.publicKey.key,
          label: this.unencodedHexToAddress(nextProps.publicKey.key)
        }
      })
    }

    if (progress !== nextProps.progress || error !== nextProps.error) {
      this.setState(this.computeStateFromProps(nextProps))
    }
  }

  computeStateFromProps = (props: Props) => {
    if (props.progress === LOADED) {
      if (this.intervalId) {
        clearInterval(this.intervalId)
      }
      return {
        ledgerStage: CONNECTED,
        isLoading: false
      }
    }
    if (props.progress === FAILED && props.error) {
      return {
        isLoading: true,
        ledgerStage:
          props.error === MESSAGES.APP_CLOSED ? OPEN_APP : NOT_CONNECTED
      }
    }
    return {
      isLoading: true,
      ledgerStage: NOT_CONNECTED
    }
  }

  componentWillUnmount() {
    if (this.intervalId) {
      clearInterval(this.intervalId)
    }
  }

  render() {
    const options = this.createOptionsFromKeys()
    const { loadingPublicKeys, publicKeys } = this.state

    return (
      <div id="loginLedgerNanoS" className={styles.flexContainer}>
        <form>
          {this.renderStatus()}
          <Label
            label="public address:"
            renderAdditionalContent={this.renderAdditionalLabelContent}
          />
          <StyledReactSelect
            value={this.state.addressOption}
            isDisabled={publicKeys.length === 1}
            onChange={addressOption => this.setState({ addressOption })}
            options={options}
            onMenuScrollToBottom={this.fetchAdditionalKeys}
            isSearchable
            isLoading={loadingPublicKeys}
          />
          <Button
            id="loginButton"
            primary
            type="submit"
            className={styles.loginButtonMargin}
            renderIcon={LoginIcon}
            disabled={!this.canLogin()}
            onClick={this.handleLogin}
            shouldCenterButtonLabelText
          >
            Login
          </Button>
        </form>
      </div>
    )
  }

  fetchAdditionalKeys = async () => {
    const { publicKeys } = this.state

    this.setState({ loadingPublicKeys: true, error: null })

    const lastAccountLoaded = publicKeys[publicKeys.length - 1].account

    const nextBatchOfKeys = await getPublicKeys(lastAccountLoaded + 1).catch(
      () => {
        console.error(
          'An error occurred getting additional public keys from ledger'
        )
        this.setState({
          error: FETCH_ADDITIONAL_KEYS_ERROR,
          loadingPublicKeys: false
        })
      }
    )

    if (nextBatchOfKeys) {
      this.setState(state => ({
        publicKeys: [...state.publicKeys, ...nextBatchOfKeys],
        loadingPublicKeys: false
      }))
    }
  }

  renderAdditionalLabelContent = () => {
    const { loadingPublicKeys, error } = this.state
    if (!loadingPublicKeys && !error) {
{'fraction_non_alphanumeric': 0.11118624420401854, 'fraction_numerical': 0.0018353941267387943, 'mean_word_length': 2.61993006993007, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 37, 'https://': 0, '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': '1081142', 'n_tokens_mistral': 3086, 'n_tokens_neox': 3045, 'n_words': 622}