import { APIFetchV1 } from "util/api"; import { ConditionalLeadingSpace, PREVENT_DEFAULT } from "util/misc"; import Divider from "components/util/Divider"; import Icon from "components/util/Icon"; import Loading from "components/util/Loading"; import { TachiConfig } from "lib/config"; import React, { useEffect, useMemo, useRef, useState } from "react"; import { Link, useLocation } from "react-router-dom"; import { Game, GetGameConfig, integer, UserDocument, SongDocument, Playtype } from "tachi-common"; import { GamePT, JustChildren } from "types/react"; import QuickTooltip from "../misc/QuickTooltip"; interface SearchReturns { users?: UserDocument[]; songs: (SongDocument & { __textScore: number; game: Game })[]; } function SearchResult({ children, link, tabIndex, }: JustChildren & { link: string; tabIndex?: integer }) { return (
{children}
); } function GPTSearchResult({ user, game, playtype, tabIndex, }: { user: UserDocument; tabIndex: integer; } & GamePT) { const currentLoc = window.location.href.split(`games/${game}/${playtype}`)[1]; return (
{user.username} {user.status}
); } function SearchResults({ results }: { results: SearchReturns }) { if (!results.users?.length && !results.songs.length) { return (
Found nothing :(
); } // useParams doesn't do what i think it does, so... // here's a hack. const params = useLocation<{ game?: Game; playtype?: Playtype }>(); const { game, playtype, user } = useMemo(() => { const regexp = `/games/(${TachiConfig.games.join("|")})/[^/]*/?`; if (params.pathname.match(new RegExp(regexp, "u"))) { const hack = params.pathname.split("/games/")[1].split("/"); return { game: hack[0] as Game, playtype: hack[1] as Playtype, user: params.pathname.match(/\/dashboard\/users\/(.*?)\//u)?.[1], }; } return { game: null, playtype: null }; }, [params.pathname]); return (
{results?.users?.length ? ( <>
Users
{results.users.map((u) => game && playtype && user ? ( ) : ( {u.username} {u.status} ) )}
) : ( <> )} {results?.songs.length ? ( <>
Songs
{results.songs.map((s) => ( {FormatSongTitle(s.game, s)} {GetGameConfig(s.game).name} ))}
) : ( <> )}
); } function FormatSongTitle(game: Game, song: SongDocument) { if (game === "bms" || game === "pms") { const s = song as SongDocument<"bms" | "pms">; return `${song.title}${ConditionalLeadingSpace(s.data.subtitle)}${ConditionalLeadingSpace( s.data.tableString )}`; } if (game === "itg") { const s = song as SongDocument<"itg">; return `${song.title}${ConditionalLeadingSpace(s.data.subtitle)}`; } return song.title; } export default function SearchBar() { const [show, setShow] = useState(false); const [search, setSearch] = useState(""); const [results, setResults] = useState(null); const [lastTimeout, setLastTimeout] = useState(null); const ref = useRef(null); const inputRef = useRef(null); // onclick outside useEffect(() => { function clickOutside(event: MouseEvent) { // @ts-expect-error lazy if (ref.current && !ref.current.contains(event.target)) { setShow(false); } } document.addEventListener("mousedown", clickOutside); return () => { document.removeEventListener("mousedown", clickOutside); }; }, [ref]); useEffect(() => { setShow(search !== ""); }, [search]); // debouncer result-getter function UpdateSearch(event: React.ChangeEvent) { setSearch(event.target.value); if (lastTimeout !== null) { clearTimeout(lastTimeout); } const closureSearch = event.target.value; // compatibility note - we use window here to specify to typescript // that this is NOT the node version of setTimeout() (which does not return a number). const handle = window.setTimeout(() => { const searches = []; searches.push( APIFetchV1(`/search?search=${encodeURIComponent(closureSearch)}`) ); if (TachiConfig.type !== "ktchi") { searches.push( APIFetchV1( `/search/chart-hash?search=${encodeURIComponent(closureSearch)}` ) ); } Promise.all(searches).then((results) => { const setValue: SearchReturns = { users: [], songs: [], }; for (const result of results) { if (result.success === false) { console.error(result); return; } setValue.songs.push(...result.body.songs); setValue.users!.push(...(result.body.users ?? [])); } setResults(setValue); }); }, 250); setLastTimeout(handle); } return (
{ if (search !== "") { setShow(true); } }} placeholder="Search users, songs, charts..." />
{results ? : }
); }