rsvp-reader/src/components/SearchBar.js

82 lines
2.3 KiB
JavaScript

import React, { useState, useEffect, useCallback } from 'react'
import { debounce } from 'debounce'
import classNames from 'classnames'
import { FiSearch } from 'react-icons/fi'
import { useDispatch } from 'react-redux'
import { useTranslation } from 'react-i18next'
import axios from 'axios'
import { search } from '../lib/gutenberg'
import { Spinner } from '../styles/Spinner'
import { Dropdown } from './generics/Dropdown'
import { Book } from './Book'
import styles from './SearchBar.css'
import { CursorList } from './generics/CursorList'
import { setText, setLang } from '../store/actions.js'
export const SearchBar = () => {
const { t } = useTranslation()
const dispatch = useDispatch()
const [searchTerm, setSearchTerm] = useState('')
const [results, setResults] = useState([])
const [loading, setLoading] = useState(false)
const [isFocused, setFocus] = useState(false)
const debouncedSearch = useCallback(
debounce(async term => {
await search(term, 100).then(setResults)
setLoading(false)
}, 500),
[]
)
const handleClick = async idx => {
const { id, language } = results[idx]
const url = `https://gutenberg.muperfredi.de/texts/${id}/stripped-body`
const text = await axios.get(url).then(res => res.data.body)
dispatch(setText(text))
dispatch(setLang(language[0]))
}
useEffect(() => {
if (searchTerm.length > 0) {
setLoading(true)
debouncedSearch(searchTerm)
} else {
setLoading(false)
}
}, [searchTerm, debouncedSearch])
return (
<>
<div>
<label className={styles.label}>
<input
type="text"
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
className={classNames({
[styles.loading]: false,
[styles.search]: true
})}
onFocus={() => setFocus(true)}
onBlur={() => setFocus(false)}
placeholder={t('search') + '...'}
></input>
{loading ? <Spinner /> : <FiSearch />}
</label>
<Dropdown visible={isFocused && searchTerm.length !== 0}>
<CursorList
items={results.map(entry => (
<Book {...entry} />
))}
onSelect={handleClick}
/>
</Dropdown>
</div>
</>
)
}