<commit_before>/**
 * @providesModule Results
 * @flow
 */

import * as React from 'react'
import qs from 'qs'
import { map } from 'ramda'

import { Orchard } from 'shared/orchard'

import { Route } from 'react-router-dom'
import { NonIdealState } from '@blueprintjs/core'
import { injectIntl, FormattedMessage } from 'react-intl'

import CaseList from 'catalog/CaseList'
import LibraryInfo from 'catalog/LibraryInfo'
import SearchForm from 'catalog/SearchForm'
import { Main, CatalogSection, SectionTitle } from 'catalog/shared'
import { Container as Sidebar } from 'catalog/Sidebar'

import type { ContextRouter } from 'react-router-dom'
import type { State } from 'catalog'

export type Query = { [string]: string[] }

type Props = {
  ...ContextRouter,
  ...State,
  readerIsEditor: boolean,
}
class Results extends React.Component<
  Props,
  { loading: boolean, results: string[] }
> {
  constructor (props: Props) {
    super(props)
    this.state = { loading: true, results: [] }
  }

  componentDidMount () {
    this._fetchResults()
  }

  componentDidUpdate (prevProps: Props) {
    if (
      this.props.location.pathname !== prevProps.location.pathname ||
      this.props.location.search !== prevProps.location.search
    ) {
      this.setState({ loading: true })
      this._fetchResults()
    }
  }

  render () {
    const { loading, cases, readerIsEditor } = this.props
    return [
      <Sidebar key="sidebar">
        <Route
          path="/catalog/libraries/:slug"
          render={({ match }) => <LibraryInfo slug={match.params.slug || ''} />}
        />
        <SearchForm params={this._getQueryParams()} />
      </Sidebar>,
      <Main key="main">
        <CatalogSection>
          <SectionTitle>
            <FormattedMessage id="search.results" />
          </SectionTitle>
          {loading.cases || this.state.loading ? null : this.state.results
            .length === 0 ? (
              <NoSearchResults />
            ) : (
              <CaseList
                cases={this.state.results.map(slug => cases[slug])}
                readerIsEditor={readerIsEditor}
              />
            )}
        </CatalogSection>
      </Main>,
    ]
  }

  _fetchResults = () => {
    Orchard.harvest(`search`, this._getQueryParams()).then(
      (results: string[]) => this.setState({ loading: false, results })
    )
  }

  _getQueryParams = (): Query => {
    const { pathname, search } = this.props.location
    return {
      ...coerceIntoArrayValues(qs.parse(search, { ignoreQueryPrefix: true })),
      ...getQueryFromPathname(pathname),
    }
  }
}
export default Results

function coerceIntoArrayValues (params: {
  [string]: string | string[],
}): { [string]: string[] } {
  return map(x => (Array.isArray(x) ? x : [x]), params)
}

function getQueryFromPathname (pathname: string): { [string]: string[] } {
  return ['libraries', 'tags'].reduce((params, key) => {
    const match = pathname.match(RegExp(`${key}/([0-9a-z%+-]+)`))
    if (!match) return params
    params[key] = [match[1]]
    return params
  }, {})
}

const NoSearchResults = injectIntl(({ intl }) => (
  <NonIdealState
    className="pt-dark"
    title={intl.formatMessage({
      id: 'search.noResults',
    })}
    description={<commit_msg>fix: don’t explode when search results return a case the user can’t see<commit_after>/**
 * @providesModule Results
 * @flow
 */

import * as React from 'react'
import qs from 'qs'
import { map } from 'ramda'

import { Orchard } from 'shared/orchard'

import { Route } from 'react-router-dom'
import { NonIdealState } from '@blueprintjs/core'
import { injectIntl, FormattedMessage } from 'react-intl'

import CaseList from 'catalog/CaseList'
import LibraryInfo from 'catalog/LibraryInfo'
import SearchForm from 'catalog/SearchForm'
import { Main, CatalogSection, SectionTitle } from 'catalog/shared'
import { Container as Sidebar } from 'catalog/Sidebar'

import type { ContextRouter } from 'react-router-dom'
import type { State } from 'catalog'

export type Query = { [string]: string[] }

type Props = {
  ...ContextRouter,
  ...State,
  readerIsEditor: boolean,
}
class Results extends React.Component<
  Props,
  { loading: boolean, results: string[] }
> {
  constructor (props: Props) {
    super(props)
    this.state = { loading: true, results: [] }
  }

  componentDidMount () {
    this._fetchResults()
  }

  componentDidUpdate (prevProps: Props) {
    if (
      this.props.location.pathname !== prevProps.location.pathname ||
      this.props.location.search !== prevProps.location.search
    ) {
      this.setState({ loading: true })
      this._fetchResults()
    }
  }

  render () {
    const { loading, readerIsEditor } = this.props
    return [
      <Sidebar key="sidebar">
        <Route
          path="/catalog/libraries/:slug"
          render={({ match }) => <LibraryInfo slug={match.params.slug || ''} />}
        />
        <SearchForm params={this._getQueryParams()} />
      </Sidebar>,
      <Main key="main">
        <CatalogSection>
          <SectionTitle>
            <FormattedMessage id="search.results" />
          </SectionTitle>
          {loading.cases || this.state.loading ? null : this._results()
            .length === 0 ? (
              <NoSearchResults />
            ) : (
              <CaseList cases={this._results()} readerIsEditor={readerIsEditor} />
            )}
        </CatalogSection>
      </Main>,
    ]
  }

  _fetchResults = () => {
    Orchard.harvest(`search`, this._getQueryParams()).then(
      (results: string[]) => this.setState({ loading: false, results })
    )
  }

  _getQueryParams = (): Query => {
    const { pathname, search } = this.props.location
    return {
      ...coerceIntoArrayValues(qs.parse(search, { ignoreQueryPrefix: true })),
      ...getQueryFromPathname(pathname),
    }
  }

  _results () {
    return this.state.results
      .map(slug => this.props.cases[slug])
      .filter(Boolean)
  }
}
export default Results

function coerceIntoArrayValues (params: {
  [string]: string | string[],
}): { [string]: string[] } {
  return map(x => (Array.isArray(x) ? x : [x]), params)
}

function getQueryFromPathname (pathname: string): { [string]: string[] } {
  return ['libraries', 'tags'].reduce((params, key) => {
    const match = pathname.match(RegExp(`${key}/([0-9a-z%+-]+)`))
    if (!match) return params
    params[key] = [match[1]]
    return params
  }, {})
}

const NoSearchResults = injectIntl(({ intl }) => (
  <NonIdealState
    className="pt-dark"
    title={intl.formatMessage({
      id: 'search.noResults',
    })}
    description={
{'fraction_non_alphanumeric': 0.1523419736243747, 'fraction_numerical': 0.0012126724268606943, 'mean_word_length': 2.840512223515716, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 49, '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': '3084321', 'n_tokens_mistral': 2028, 'n_tokens_neox': 2017, 'n_words': 447}