<commit_before>// @flow

import Icon from '@conveyal/woonerf/components/icon'
import React, {Component} from 'react'
import {ListGroupItem, Panel, Badge, ListGroup, FormControl, ButtonGroup} from 'react-bootstrap'
import {Link} from 'react-router'

import OptionButton from '../../common/components/OptionButton'
import {getComponentMessages, getMessage} from '../../common/util/config'
import toSentenceCase from '../../common/util/to-sentence-case'
import {getAbbreviatedProjectName} from '../../common/util/util'

import type {Feed, Project} from '../../types'
import type {UserState} from '../reducers/user'

type Props = {
  activeProject: Project,
  visibilityFilter: any,
  searchTextChanged: string => void,
  user: UserState,
  visibilityFilterChanged: string => void
}
export default class FeedSourcePanel extends Component<Props> {
  _feedVisibilityFilter = (feed: Feed) => {
    const {visibilityFilter} = this.props
    const name = feed.name || 'unnamed'
    const visible = name.toLowerCase().indexOf((visibilityFilter.searchText || '').toLowerCase()) !== -1
    switch (visibilityFilter.filter) {
      case 'ALL':
        return visible
      case 'STARRED':
        return [].indexOf(feed.id) !== -1 // check userMetaData
      case 'PUBLIC':
        return feed.isPublic
      case 'PRIVATE':
        return !feed.isPublic
      default:
        return visible
    }
    // if (feed.isCreating) return true // feeds actively being created are always visible
  }

  renderFeedItems = (p: Project, fs: Feed) => {
    const feedName = `${getAbbreviatedProjectName(p)} / ${fs.name}`
    return (
      <ListGroupItem key={fs.id} bsStyle={fs.isPublic ? null : 'warning'}>
        <Link title={feedName} to={`/feed/${fs.id}`}>
          <Icon className='icon-link' type={fs.isPublic ? 'database' : 'lock'} />
          <span style={{ fontSize: 16, fontWeight: 500 }}>
            {feedName.length > 33 ? `${feedName.substr(0, 33)}...` : feedName}
          </span>
        </Link>
      </ListGroupItem>
    )
  }

  _onChangeSearch = (evt: SyntheticInputEvent<HTMLInputElement>) =>
    this.props.searchTextChanged(evt.target.value)

  render () {
    const messages = getComponentMessages('FeedSourcePanel')
    const {
      activeProject,
      visibilityFilter,
      user,
      visibilityFilterChanged
    } = this.props
    const BUTTON_FILTERS = ['ALL', 'STARRED', 'PUBLIC', 'PRIVATE']
    const isProjectAdmin = user.permissions &&
      user.permissions.isProjectAdmin(
        activeProject.id,
        activeProject.organizationId
      )
    return (
      <Panel header={
        <h3>Feeds for{' '}
          {activeProject
            ? getAbbreviatedProjectName(activeProject)
            : '[choose project]'
          }{' '}
          {activeProject && activeProject.feedSources &&
            <Badge>{activeProject.feedSources.length}</Badge>
          }
        </h3>
      }>
        <ListGroup fill>
          <ListGroupItem>
            <FormControl
              placeholder={getMessage(messages, 'search')}
              onChange={this._onChangeSearch} />
            <ButtonGroup style={{marginTop: 10}} justified>
              {BUTTON_FILTERS.map(b => (
                <OptionButton
                  active={visibilityFilter.filter === b || (b === 'ALL' && !visibilityFilter.filter)}
                  bsSize='xsmall'
                  key={b}
                  onClick={visibilityFilterChanged}
                  value={b}
                  href='#'>{toSentenceCase(b)}</OptionButton>
              ))}
            </ButtonGroup>
          </ListGroupItem>
          {activeProject && activeProject.feedSources
            ? activeProject.feedSources
              .filter(this._feedVisibilityFilter)<commit_msg>refactor(misc): fix NPE bug introduced on missing activeProject<commit_after>// @flow

import Icon from '@conveyal/woonerf/components/icon'
import React, {Component} from 'react'
import {ListGroupItem, Panel, Badge, ListGroup, FormControl, ButtonGroup} from 'react-bootstrap'
import {Link} from 'react-router'

import OptionButton from '../../common/components/OptionButton'
import {getComponentMessages, getMessage} from '../../common/util/config'
import toSentenceCase from '../../common/util/to-sentence-case'
import {getAbbreviatedProjectName} from '../../common/util/util'

import type {Feed, Project} from '../../types'
import type {UserState} from '../reducers/user'

type Props = {
  activeProject: Project,
  visibilityFilter: any,
  searchTextChanged: string => void,
  user: UserState,
  visibilityFilterChanged: string => void
}
export default class FeedSourcePanel extends Component<Props> {
  _feedVisibilityFilter = (feed: Feed) => {
    const {visibilityFilter} = this.props
    const name = feed.name || 'unnamed'
    const searchText = (visibilityFilter.searchText || '').toLowerCase()
    const visible = name.toLowerCase().indexOf(searchText) !== -1
    switch (visibilityFilter.filter) {
      case 'ALL':
        return visible
      case 'STARRED':
        return [].indexOf(feed.id) !== -1 // FIXME check userMetaData
      case 'PUBLIC':
        return feed.isPublic
      case 'PRIVATE':
        return !feed.isPublic
      default:
        return visible
    }
    // if (feed.isCreating) return true // feeds actively being created are always visible
  }

  renderFeedItems = (p: Project, fs: Feed) => {
    const feedName = `${getAbbreviatedProjectName(p)} / ${fs.name}`
    return (
      <ListGroupItem key={fs.id} bsStyle={fs.isPublic ? null : 'warning'}>
        <Link title={feedName} to={`/feed/${fs.id}`}>
          <Icon className='icon-link' type={fs.isPublic ? 'database' : 'lock'} />
          <span style={{ fontSize: 16, fontWeight: 500 }}>
            {feedName.length > 33 ? `${feedName.substr(0, 33)}...` : feedName}
          </span>
        </Link>
      </ListGroupItem>
    )
  }

  _onChangeSearch = (evt: SyntheticInputEvent<HTMLInputElement>) =>
    this.props.searchTextChanged(evt.target.value)

  render () {
    const messages = getComponentMessages('FeedSourcePanel')
    const {
      activeProject,
      visibilityFilter,
      user,
      visibilityFilterChanged
    } = this.props
    const BUTTON_FILTERS = ['ALL', 'STARRED', 'PUBLIC', 'PRIVATE']
    const isProjectAdmin = user.permissions && activeProject &&
      user.permissions.isProjectAdmin(
        activeProject.id,
        activeProject.organizationId
      )
    return (
      <Panel header={
        <h3>Feeds for{' '}
          {activeProject
            ? getAbbreviatedProjectName(activeProject)
            : '[choose project]'
          }{' '}
          {activeProject && activeProject.feedSources &&
            <Badge>{activeProject.feedSources.length}</Badge>
          }
        </h3>
      }>
        <ListGroup fill>
          <ListGroupItem>
            <FormControl
              placeholder={getMessage(messages, 'search')}
              onChange={this._onChangeSearch} />
            <ButtonGroup style={{marginTop: 10}} justified>
              {BUTTON_FILTERS.map(b => (
                <OptionButton
                  active={visibilityFilter.filter === b ||
                    (b === 'ALL' && !visibilityFilter.filter)
                  }
                  bsSize='xsmall'
                  key={b}
                  onClick={visibilityFilterChanged}
                  value={b}
                  href='#'>{toSentenceCase(b)}</OptionButton>
              ))}
            </ButtonGroup>
          </ListGroupItem>
          {activeProject && activeProject.feedSources
            ? activeProject.feedSources
              .filter(this._feedVisibilityFilter)
{'fraction_non_alphanumeric': 0.13159268929503917, 'fraction_numerical': 0.004177545691906005, 'mean_word_length': 2.447794779477948, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 61, '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': '7577025', 'n_tokens_mistral': 2176, 'n_tokens_neox': 2118, 'n_words': 465}