import React from "react" import * as antd from "antd" import classnames from "classnames" import lodash from "lodash" import { Translation } from "react-i18next" import { Icons, createIconRender } from "@components/Icons" import useUrlQueryActiveKey from "@hooks/useUrlQueryActiveKey" import UserPreview from "@components/UserPreview" import MusicTrack from "@components/Music/Track" import Playlist from "@components/Music/Playlist" import SearchModel from "@models/search" import "./index.less" const ResultsTypeDecorators = { users: { icon: "FiUsers", label: "Users", onClick: (item) => { app.navigation.goToAccount(item.username) }, renderItem: (props) => { const { item, onClick } = props return (
onClick(item)} user={item} />
) }, }, tracks: { icon: "MdAlbum", label: "Tracks", renderItem: (props) => { const { item, onClick } = props return (
) }, }, playlists: { icon: "MdPlaylistPlay", label: "Playlists", renderItem: (props) => { return (
) }, }, } const Results = (props) => { let { results } = props // console.log("results", results, typeof results) if (typeof results !== "object") { return null } let groupsKeys = Object.keys(results) // filter out groups with no items array property groupsKeys = groupsKeys.filter((key) => { if (!Array.isArray(results[key].items)) { return false } return true }) // filter out groups with empty items array groupsKeys = groupsKeys.filter((key) => { return results[key].items.length > 0 }) const handleClick = async (decorator, data) => { if (typeof decorator.onClick === "function") { await decorator.onClick(data) } if (typeof props.onClose === "function") { return props.onClose() } } if (props.loading) { return (
) } if (groupsKeys.length === 0) { return (
) } return (
{groupsKeys.map((key, index) => { const decorator = ResultsTypeDecorators[key] ?? { icon: null, label: key, renderItem: () => null, } return (

{createIconRender(decorator.icon)} {(t) => t(decorator.label)}

{results[key].items.map((item, index) => { return decorator.renderItem({ key: index, item, onClick: (...data) => handleClick(decorator, ...data), ...decorator.props, }) })}
) })}
) } const Searcher = (props) => { const [loading, setLoading] = React.useState(false) const [searchResult, setSearchResult] = React.useState(null) const [searchValue, setSearchValue] = React.useState("") const [query, setQuery] = useUrlQueryActiveKey({ queryKey: "search", defaultKey: null, }) const makeSearch = async (value) => { if (value === "") { return setSearchResult(null) } setLoading(true) if (props.useUrlQuery) { setQuery(value) } let result = null if (typeof props.model === "function") { result = await props.model(value, { ...props.modelParams, limit: app.isMobile ? 3 : 5, }) } else { result = await SearchModel.search(value, { ...props.modelParams, limit: app.isMobile ? 3 : 5, }) } if (typeof props.onSearchResult === "function") { await props.onSearchResult(result) } setLoading(false) return setSearchResult(result) } const debounceSearch = React.useCallback( lodash.debounce(makeSearch, 500), [], ) const handleOnSearch = (e) => { // not allow to input space as first character if (e.target.value[0] === " ") { return } setSearchValue(e.target.value) if (e.target.value === "") { debounceSearch.cancel() if (props.useUrlQuery) { setQuery(null) } if (typeof props.onEmpty === "function") { props.onEmpty() } } else { if (typeof props.onFilled === "function") { props.onFilled() } debounceSearch(e.target.value) } } React.useEffect(() => { if (props.useUrlQuery) { if (typeof query === "string") { makeSearch(query) setSearchValue(query) } } }, []) return (
} autoFocus={props.autoFocus ?? false} onFocus={props.onFocus} onBlur={props.onUnfocus} /> {searchResult && props.renderResults && ( )}
) } export default Searcher