<commit_before>import '../../../css/browser/side-bar-tags'
import '../../../css/auto-suggest'

import React, { PropTypes } from 'react'

import { findDOMNode } from 'react-dom'
import { connect } from 'react-redux'
import { FormattedMessage as T } from 'react-intl'
import cx from 'classnames'
import Autosuggest from 'react-autosuggest'
import { intlShape } from 'react-intl'
import Button from '../../Button'

import { addTagsCategory, addTag, removeTag, fetchTags } from '../../../actions/tags'


class SideBarTags extends React.Component {

  constructor (props) {
    super(props)

    this.state = {
      tagValue: {}, // [category or (category + '/' + value)]: string
      fullSuggestions: {}, // [category]: Array<string>
      currentSuggestions: [], // Array<string>
      newCategory: ''
      // ['edit/' + category + '/' + value] : true
    }

    // Force-bind methods used like `onEvent={ this.method }`
    this.addCategory = this.addCategory.bind(this)
    this.onChangeNewCategory = this.onChangeNewCategory.bind(this)
    this.renderTagsCategory = this.renderTagsCategory.bind(this)
  }

  componentWillMount () {
    this.updateFullSuggestions(this.props.categories)
  }

  componentReceiveProps ({ categories }) {
    if (JSON.stringify(categories) !== JSON.stringify(this.props.categories)) {
      this.updateFullSuggestions(categories)
    }
  }

  updateFullSuggestions (categories) {
    // Updated categories, fetch suggestions
    categories.forEach((category) => {
      this.fetchFullSuggestions(category)
    })
  }

  onChangeNewCategory (e) {
    this.setState({ newCategory: e.target.value })
  }

  addCategory (e) {
    e.preventDefault()

    const { newCategory } = this.state
    if (newCategory) {
      const { serverUrl, corpusId, addTagsCategory } = this.props
      addTagsCategory(serverUrl, corpusId, this.state.newCategory)
      this.setState({ newCategory: '' })
    } else {
      findDOMNode(this).querySelector('form.tags-new-category input').focus()
    }
  }

  renderTagsCategory (category) {
    const tags = (this.props.webentity.tags.USER || {})[category] || []
    const isFreeTags = (category === 'FREETAGS')
    const freeTagsTitle = this.context.intl.formatMessage({ id: 'sidebar.freetags' })
    const canAddTag = isFreeTags || tags.length === 0

    return (
      <li key={ category }>
        <h3>{ isFreeTags ? freeTagsTitle : category }</h3>
        <ul>
          { tags.map(this.renderTag(category)) }
        </ul>
        { canAddTag ? this.renderTagInput(category) : <noscript /> }
      </li>
    )
  }

  fetchFullSuggestions (category) {
    const { serverUrl, corpusId, fetchTags } = this.props
    fetchTags(serverUrl, corpusId, category).then((tags) => {
      this.setState({
        fullSuggestions: {
          ...this.state.fullSuggestions,
          [category]: tags
        }
      })
    })
  }

  addTagHandler (category, tag) {
    return (e) => {
      e.preventDefault()
      return this.addTag(category, tag)
    }
  }

  addTag (category, tag) {
    const { serverUrl, corpusId, webentity, addTag } = this.props
    const value = this.getEditedTagValue(category, tag)
    this.changeEditedTagValue(category, '', tag)
    return addTag(serverUrl, corpusId, category, webentity.id, value, tag).then(() => {
      // Keep suggestions up to date
      this.setState({
        fullSuggestions: {
          ...this.state.fullSuggestions,
          [category]: (this.state.fullSuggestions[category] || []).concat(value)
        }
      })
    })
  }

  updateTagHandler (category, tag) {
    return (e) => {
      e.preventDefault()
      this.updateTag(category, tag)
    }
  }

  updateTag (category, tag) {
    const value = this.getEditedTagValue(category, tag)
    if (value === tag) {
      this.changeEditedTagValue(category, '', tag)
      return Promise.resolve()
    }

    return this.addTag(category, tag).then(() => this.removeTag(category, tag))
  }

  removeTag (category, value) {
    const { serverUrl, corpusId, webentity, removeTag } = this.props
    return removeTag(serverUrl, corpusId, category, webentity.id, value)
  }

  changeEditedTagValue (category, value, tag) {
    const prop = category + (tag ? ('/' + tag) : '')
    this.setState({
      tagValue: {
        ...this.state.tagValue,
        [prop]: value
      }
    })
  }

  getEditedTagValue (category, tag) {
    const prop = category + (tag ? ('/' + tag) : '')
    return (typeof this.state.tagValue[prop] === 'string')
      ? this.state.tagValue[prop]
      : tag
  }

  renderTagInput (category, tag = null) {
    const { formatMessage } = this.context.intl
    const uniqSuffix = category + (tag ? ('-' + tag) : '')

    return (
      <form
        className={ cx('btn-group', { 'tags-new-tag': !tag, 'tags-edit-tag': !!tag }) }
        onSubmit={ tag ? this.updateTagHandler(category, tag) : this.addTagHandler(category) }
        >
        <Autosuggest
          id={ 'tags-' + uniqSuffix }
          suggestions={ this.state.currentSuggestions }
          onSuggestionsUpdateRequested={ ({ value }) => this.setState({ currentSuggestions: getSuggestions(this.state.fullSuggestions[category], value) }) }
          getSuggestionValue={ getSuggestionValue }
          renderSuggestion={ renderSuggestion }
          shouldRenderSuggestions={ () => true }
          inputProps={ {
            className: 'form-control btn btn-large tag-input-' + uniqSuffix,
            placeholder: 'New tag',
            value: this.getEditedTagValue(category, tag),
            autoFocus: true,
            onChange: (e, { newValue }) => this.changeEditedTagValue(category, newValue, tag)
          } }
        />
      <Button size="large" icon={ tag ? 'pencil' : 'plus' } title={ formatMessage({ id: 'sidebar.add-tag' }) } />
      </form>
    )
  }

  editTag (category, tag) {
    this.setState({ ['edit/' + category + '/' + tag]: true })
  }

  isEditedTag (category, tag) {
    return !!this.state['edit/' + category + '/' + tag]
  }
<commit_msg>fix(tags): Do not mix new tag current value and edited tag value<commit_after>import '../../../css/browser/side-bar-tags'
import '../../../css/auto-suggest'

import React, { PropTypes } from 'react'

import { findDOMNode } from 'react-dom'
import { connect } from 'react-redux'
import { FormattedMessage as T } from 'react-intl'
import cx from 'classnames'
import Autosuggest from 'react-autosuggest'
import { intlShape } from 'react-intl'
import Button from '../../Button'

import { addTagsCategory, addTag, removeTag, fetchTags } from '../../../actions/tags'


class SideBarTags extends React.Component {

  constructor (props) {
    super(props)

    this.state = {
      tagValue: {}, // [category or (category + '/' + value)]: string
      fullSuggestions: {}, // [category]: Array<string>
      currentSuggestions: [], // Array<string>
      newCategory: ''
      // ['edit/' + category + '/' + value] : true
    }

    // Force-bind methods used like `onEvent={ this.method }`
    this.addCategory = this.addCategory.bind(this)
    this.onChangeNewCategory = this.onChangeNewCategory.bind(this)
    this.renderTagsCategory = this.renderTagsCategory.bind(this)
  }

  componentWillMount () {
    this.updateFullSuggestions(this.props.categories)
  }

  componentReceiveProps ({ categories }) {
    if (JSON.stringify(categories) !== JSON.stringify(this.props.categories)) {
      this.updateFullSuggestions(categories)
    }
  }

  updateFullSuggestions (categories) {
    // Updated categories, fetch suggestions
    categories.forEach((category) => {
      this.fetchFullSuggestions(category)
    })
  }

  onChangeNewCategory (e) {
    this.setState({ newCategory: e.target.value })
  }

  addCategory (e) {
    e.preventDefault()

    const { newCategory } = this.state
    if (newCategory) {
      const { serverUrl, corpusId, addTagsCategory } = this.props
      addTagsCategory(serverUrl, corpusId, this.state.newCategory)
      this.setState({ newCategory: '' })
    } else {
      findDOMNode(this).querySelector('form.tags-new-category input').focus()
    }
  }

  renderTagsCategory (category) {
    const tags = (this.props.webentity.tags.USER || {})[category] || []
    const isFreeTags = (category === 'FREETAGS')
    const freeTagsTitle = this.context.intl.formatMessage({ id: 'sidebar.freetags' })
    const canAddTag = isFreeTags || tags.length === 0

    return (
      <li key={ category }>
        <h3>{ isFreeTags ? freeTagsTitle : category }</h3>
        <ul>
          { tags.map(this.renderTag(category)) }
        </ul>
        { canAddTag ? this.renderTagInput(category) : <noscript /> }
      </li>
    )
  }

  fetchFullSuggestions (category) {
    const { serverUrl, corpusId, fetchTags } = this.props
    fetchTags(serverUrl, corpusId, category).then((tags) => {
      this.setState({
        fullSuggestions: {
          ...this.state.fullSuggestions,
          [category]: tags
        }
      })
    })
  }

  addTagHandler (category, tag = null) {
    return (e) => {
      e.preventDefault()
      return this.addTag(category, tag)
    }
  }

  addTag (category, tag = null) {
    const { serverUrl, corpusId, webentity, addTag } = this.props
    const value = this.getEditedTagValue(category, tag)
    this.changeEditedTagValue(category, '', tag)
    return addTag(serverUrl, corpusId, category, webentity.id, value, tag).then(() => {
      // Keep suggestions up to date
      this.setState({
        fullSuggestions: {
          ...this.state.fullSuggestions,
          [category]: (this.state.fullSuggestions[category] || []).concat(value)
        }
      })
    })
  }

  updateTagHandler (category, tag) {
    return (e) => {
      e.preventDefault()
      this.updateTag(category, tag)
    }
  }

  updateTag (category, tag) {
    const value = this.getEditedTagValue(category, tag)
    if (value === tag) {
      this.changeEditedTagValue(category, '', tag)
      return Promise.resolve()
    }

    return this.addTag(category, tag).then(() => this.removeTag(category, tag))
  }

  removeTag (category, value) {
    const { serverUrl, corpusId, webentity, removeTag } = this.props
    return removeTag(serverUrl, corpusId, category, webentity.id, value)
  }

  changeEditedTagValue (category, value, tag = null) {
    const prop = category + ((tag === null) ? ('/' + tag) : '')
    this.setState({
      tagValue: {
        ...this.state.tagValue,
        [prop]: value
      }
    })
  }

  getEditedTagValue (category, tag = null) {
    const prop = category + ((tag === null) ? ('/' + tag) : '')
    return (typeof this.state.tagValue[prop] === 'string')
      ? this.state.tagValue[prop]
      : tag
  }

  renderTagInput (category, tag = null) {
    const { formatMessage } = this.context.intl
    const uniqSuffix = category + ((tag === null) ? ('-' + tag) : '')

    return (
      <form
        key={ 'tag/edit/' + category + '/' + tag }
        className={ cx('btn-group', { 'tags-new-tag': !tag, 'tags-edit-tag': !!tag }) }
        onSubmit={ tag ? this.updateTagHandler(category, tag) : this.addTagHandler(category) }
        >
        <Autosuggest
          id={ 'tags-' + uniqSuffix }
          suggestions={ this.state.currentSuggestions }
          onSuggestionsUpdateRequested={ ({ value }) => this.setState({ currentSuggestions: getSuggestions(this.state.fullSuggestions[category], value) }) }
          getSuggestionValue={ getSuggestionValue }
          renderSuggestion={ renderSuggestion }
          shouldRenderSuggestions={ () => true }
          inputProps={ {
            className: 'form-control btn btn-large tag-input-' + uniqSuffix,
            placeholder: 'New tag',
            value: this.getEditedTagValue(category, tag),
            autoFocus: true,
            onChange: (e, { newValue }) => this.changeEditedTagValue(category, newValue, tag)
          } }
        />
      <Button size="large" icon={ tag ? 'pencil' : 'plus' } title={ formatMessage({ id: 'sidebar.add-tag' }) } />
      </form>
    )
  }

  editTag (category, tag) {
    this.setState({ ['edit/' + category + '/' + tag]: true })
  }

  isEditedTag (category, tag) {
    return !!this.state['edit/' + category + '/' + tag]
  }

{'fraction_non_alphanumeric': 0.1488070837091088, 'fraction_numerical': 0.0004919242436664754, 'mean_word_length': 2.7211714460036607, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 47, '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': '2641407', 'n_tokens_mistral': 3779, 'n_tokens_neox': 3696, 'n_words': 776}