<commit_before>import React, { useEffect, useState, useCallback } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { DragDropContext, Droppable } from 'react-beautiful-dnd'
import HttpApi from 'lib/HttpApi'
import Icon from 'components/Icon'
import PathChooser from './PathChooser'
import PathItem from './PathItem'
import styles from './PathPrefs.css'
import { receivePrefs, requestScan, setPathPriority } from 'store/modules/prefs'

const api = new HttpApi('prefs/path')

const PathPrefs = props => {
  const paths = useSelector(state => state.prefs.paths)

  const [isExpanded, setExpanded] = useState(true)
  const [isChoosing, setChoosing] = useState(false)

  // local state for immediate UI updates
  const [priority, setPriority] = useState(paths.result)
  useEffect(() => { setPriority(paths.result) }, [paths])

  const toggleExpanded = useCallback(() => setExpanded(!isExpanded))
  const handleCloseChooser = useCallback(() => setChoosing(false))
  const handleOpenChooser = useCallback(() => setChoosing(true))

  const dispatch = useDispatch()
  const handleDragEnd = useCallback(dnd => {
    // dropped outside the list?
    if (!dnd.destination) return

    const res = priority.slice() // copy
    const [removed] = res.splice(dnd.source.index, 1)
    res.splice(dnd.destination.index, 0, removed)

    setPriority(res)
    dispatch(setPathPriority(res))
  }, [dispatch, priority])

  const handleAddPath = useCallback(dir => {
    api('POST', `/?dir=${encodeURIComponent(dir)}`)
      .then(res => {
        dispatch(receivePrefs(res))
        setChoosing(false)
      }).catch(err => {
        alert(err)
      })
  }, [dispatch])

  const handleRemovePath = useCallback(e => {
    const { path, pathId } = paths.entities[e.currentTarget.dataset.pathId]

    if (!confirm(`Remove folder from library?\n\n${path}`)) {
      return
    }

    // optimistically update local state
    setPriority(priority.filter(id => id !== pathId))

    api('DELETE', `/${pathId}`)
      .then(res => {
        dispatch(receivePrefs(res))
      }).catch(err => {
        alert(err)
      })
  }, [dispatch, paths])

  const handleRefresh = useCallback(() => dispatch(requestScan()), [dispatch])

  return (
    <div className={styles.container}>
      <div className={styles.heading} onClick={toggleExpanded}>
        <Icon icon='FOLDER_MUSIC' size={28} className={styles.icon} />
        <div className={styles.title}>Media Folders</div>
        <div>
          <Icon icon={isExpanded ? 'CHEVRON_DOWN' : 'CHEVRON_RIGHT'} size={24} className={styles.icon} />
        </div>
      </div>

      <div className={styles.content} style={{ display: isExpanded || paths.result.length === 0 ? 'block' : 'none' }}>
        {paths.result.length === 0 &&
          <p style={{ marginTop: 0 }}>Add a media folder to get started.</p>
        }

        <DragDropContext onDragEnd={handleDragEnd}>
          <Droppable droppableId='droppable'>
            {(provided, snapshot) => (
              <div ref={provided.innerRef} {...provided.droppableProps}>
                {priority.map((pathId, i) =><commit_msg>fix: Folder prefs weren't collapsible when no paths set<commit_after>import React, { useEffect, useState, useCallback } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { DragDropContext, Droppable } from 'react-beautiful-dnd'
import HttpApi from 'lib/HttpApi'
import Icon from 'components/Icon'
import PathChooser from './PathChooser'
import PathItem from './PathItem'
import styles from './PathPrefs.css'
import { receivePrefs, requestScan, setPathPriority } from 'store/modules/prefs'

const api = new HttpApi('prefs/path')

const PathPrefs = props => {
  const paths = useSelector(state => state.prefs.paths)
  const [isExpanded, setExpanded] = useState(false)
  const [isChoosing, setChoosing] = useState(false)
  const [priority, setPriority] = useState(paths.result)

  const toggleExpanded = useCallback(() => setExpanded(!isExpanded))
  const handleCloseChooser = useCallback(() => setChoosing(false))
  const handleOpenChooser = useCallback(() => setChoosing(true))

  useEffect(() => {
    // local state for immediate UI updates
    setPriority(paths.result)

    if (paths.result.length === 0) {
      setExpanded(true)
    }
  }, [paths])

  const dispatch = useDispatch()
  const handleRefresh = useCallback(() => dispatch(requestScan()), [dispatch])
  const handleDragEnd = useCallback(dnd => {
    // dropped outside the list?
    if (!dnd.destination) return

    const res = priority.slice() // copy
    const [removed] = res.splice(dnd.source.index, 1)
    res.splice(dnd.destination.index, 0, removed)

    setPriority(res)
    dispatch(setPathPriority(res))
  }, [dispatch, priority])

  const handleAddPath = useCallback(dir => {
    api('POST', `/?dir=${encodeURIComponent(dir)}`)
      .then(res => {
        dispatch(receivePrefs(res))
        setChoosing(false)
      }).catch(err => {
        alert(err)
      })
  }, [dispatch])

  const handleRemovePath = useCallback(e => {
    const { path, pathId } = paths.entities[e.currentTarget.dataset.pathId]

    if (!confirm(`Remove folder from library?\n\n${path}`)) {
      return
    }

    // optimistically update local state
    setPriority(priority.filter(id => id !== pathId))

    api('DELETE', `/${pathId}`)
      .then(res => {
        dispatch(receivePrefs(res))
      }).catch(err => {
        alert(err)
      })
  }, [dispatch, paths])

  return (
    <div className={styles.container}>
      <div className={styles.heading} onClick={toggleExpanded}>
        <Icon icon='FOLDER_MUSIC' size={28} className={styles.icon} />
        <div className={styles.title}>Media Folders</div>
        <div>
          <Icon icon={isExpanded ? 'CHEVRON_DOWN' : 'CHEVRON_RIGHT'} size={24} className={styles.icon} />
        </div>
      </div>

      <div className={styles.content} style={{ display: isExpanded ? 'block' : 'none' }}>
        {paths.result.length === 0 &&
          <p style={{ marginTop: 0 }}>Add a media folder to get started.</p>
        }

        <DragDropContext onDragEnd={handleDragEnd}>
          <Droppable droppableId='droppable'>
            {(provided, snapshot) => (
              <div ref={provided.innerRef} {...provided.droppableProps}>
                {priority.map((pathId, i) =>
{'fraction_non_alphanumeric': 0.14826498422712933, 'fraction_numerical': 0.0028391167192429023, 'mean_word_length': 3.3943173943173943, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 67, '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': '1789446', 'n_tokens_mistral': 1977, 'n_tokens_neox': 1912, 'n_words': 438}