import React, { useEffect, useState } from "react"; import { getCookie, setCookie } from "cookies-next"; import { useRouter } from "next/router"; import { useTranslation } from "react-i18next"; import InfiniteScroll from "react-infinite-scroll-component"; import api from "~utils/api"; import * as Dialog from "@radix-ui/react-dialog"; import CharacterSearchFilterBar from "~components/CharacterSearchFilterBar"; import WeaponSearchFilterBar from "~components/WeaponSearchFilterBar"; import SummonSearchFilterBar from "~components/SummonSearchFilterBar"; import JobSkillSearchFilterBar from "~components/JobSkillSearchFilterBar"; import CharacterResult from "~components/CharacterResult"; import WeaponResult from "~components/WeaponResult"; import SummonResult from "~components/SummonResult"; import JobSkillResult from "~components/JobSkillResult"; import type { SearchableObject, SearchableObjectArray } from "~types"; import "./index.scss"; import CrossIcon from "~public/icons/Cross.svg"; import cloneDeep from "lodash.clonedeep"; interface Props { send: (object: SearchableObject, position: number) => any; placeholderText: string; fromPosition: number; job?: Job; object: "weapons" | "characters" | "summons" | "job_skills"; children: React.ReactNode; } const SearchModal = (props: Props) => { // Set up router const router = useRouter(); const locale = router.locale; // Set up translation const { t } = useTranslation("common"); let searchInput = React.createRef(); let scrollContainer = React.createRef(); const [firstLoad, setFirstLoad] = useState(true); const [filters, setFilters] = useState<{ [key: string]: any }>(); const [open, setOpen] = useState(false); const [query, setQuery] = useState(""); const [results, setResults] = useState([]); // Pagination states const [recordCount, setRecordCount] = useState(0); const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(1); useEffect(() => { if (searchInput.current) searchInput.current.focus(); }, [searchInput]); function inputChanged(event: React.ChangeEvent) { const text = event.target.value; if (text.length) { setQuery(text); } else { setQuery(""); } } function fetchResults({ replace = false }: { replace?: boolean }) { api .search({ object: props.object, query: query, job: props.job?.id, filters: filters, locale: locale, page: currentPage, }) .then((response) => { setTotalPages(response.data.total_pages); setRecordCount(response.data.count); if (replace) { replaceResults(response.data.count, response.data.results); } else { appendResults(response.data.results); } }) .catch((error) => { console.error(error); }); } function replaceResults(count: number, list: SearchableObjectArray) { if (count > 0) { setResults(list); } else { setResults([]); } } function appendResults(list: SearchableObjectArray) { setResults([...results, ...list]); } function storeRecentResult(result: SearchableObject) { const key = `recent_${props.object}`; const cookie = getCookie(key); const cookieObj: SearchableObjectArray = cookie ? JSON.parse(cookie as string) : []; let recents: SearchableObjectArray = []; if (props.object === "weapons") { recents = cloneDeep(cookieObj as Weapon[]) || []; if ( !recents.find( (item) => (item as Weapon).granblue_id === (result as Weapon).granblue_id ) ) { recents.unshift(result as Weapon); } } else if (props.object === "summons") { recents = cloneDeep(cookieObj as Summon[]) || []; if ( !recents.find( (item) => (item as Summon).granblue_id === (result as Summon).granblue_id ) ) { recents.unshift(result as Summon); } } if (recents && recents.length > 5) recents.pop(); setCookie(`recent_${props.object}`, recents, { path: "/" }); sendData(result); } function sendData(result: SearchableObject) { props.send(result, props.fromPosition); openChange(); } function receiveFilters(filters: { [key: string]: any }) { setCurrentPage(1); setResults([]); setFilters(filters); } useEffect(() => { // Current page changed if (open && currentPage > 1) { fetchResults({ replace: false }); } else if (open && currentPage == 1) { fetchResults({ replace: true }); } }, [currentPage]); useEffect(() => { // Filters changed const key = `recent_${props.object}`; const cookie = getCookie(key); const cookieObj: Weapon[] | Summon[] | Character[] = cookie ? JSON.parse(cookie as string) : []; if (open) { if (firstLoad && cookieObj && cookieObj.length > 0) { setResults(cookieObj); setRecordCount(cookieObj.length); setFirstLoad(false); } else { setCurrentPage(1); fetchResults({ replace: true }); } } }, [filters]); useEffect(() => { // Query changed if (open && query.length != 1) { setCurrentPage(1); fetchResults({ replace: true }); } }, [query]); function renderResults() { let jsx; switch (props.object) { case "weapons": jsx = renderWeaponSearchResults(); break; case "summons": jsx = renderSummonSearchResults(results); break; case "characters": jsx = renderCharacterSearchResults(results); break; case "job_skills": jsx = renderJobSkillSearchResults(results); break; } return ( 0 ? results.length : 0} next={() => setCurrentPage(currentPage + 1)} hasMore={totalPages > currentPage} scrollableTarget="Results" loader={
Loading...
} > {jsx}
); } function renderWeaponSearchResults() { let jsx: React.ReactNode; const castResults: Weapon[] = results as Weapon[]; if (castResults && Object.keys(castResults).length > 0) { jsx = castResults.map((result: Weapon) => { return ( { storeRecentResult(result); }} /> ); }); } return jsx; } function renderSummonSearchResults(results: { [key: string]: any }) { let jsx: React.ReactNode; const castResults: Summon[] = results as Summon[]; if (castResults && Object.keys(castResults).length > 0) { jsx = castResults.map((result: Summon) => { return ( { storeRecentResult(result); }} /> ); }); } return jsx; } function renderCharacterSearchResults(results: { [key: string]: any }) { let jsx: React.ReactNode; const castResults: Character[] = results as Character[]; if (castResults && Object.keys(castResults).length > 0) { jsx = castResults.map((result: Character) => { return ( { storeRecentResult(result); }} /> ); }); } return jsx; } function renderJobSkillSearchResults(results: { [key: string]: any }) { let jsx: React.ReactNode; const castResults: JobSkill[] = results as JobSkill[]; if (castResults && Object.keys(castResults).length > 0) { jsx = castResults.map((result: JobSkill) => { return ( { storeRecentResult(result); }} /> ); }); } return jsx; } function openChange() { if (open) { setQuery(""); setFirstLoad(true); setResults([]); setRecordCount(0); setCurrentPage(1); setOpen(false); } else { setOpen(true); } } return ( {props.children}
{t("search.result_count", { record_count: recordCount })}
{open ? renderResults() : ""}
); }; export default SearchModal;