import { GetStaticProps } from "next"; import { useCallback, useEffect, useState } from "react"; import { useBoolean } from "usehooks-ts"; import { z } from "zod"; import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { NavOption } from "components/PanelComponents/NavOption"; import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { SubPanel } from "components/Containers/SubPanel"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel"; import { HorizontalLine } from "components/HorizontalLine"; import { Button } from "components/Inputs/Button"; import { Switch } from "components/Inputs/Switch"; import { TextInput } from "components/Inputs/TextInput"; import { WithLabel } from "components/Inputs/WithLabel"; import { useDeviceSupportsHover } from "hooks/useMediaQuery"; import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts"; import { prettySlug } from "helpers/formatters"; import { getOpenGraph } from "helpers/openGraph"; import { TranslatedPreviewCard } from "components/PreviewCard"; import { sendAnalytics } from "helpers/analytics"; import { useTypedRouter } from "hooks/useTypedRouter"; import { MeiliIndices, MeiliWikiPage } from "shared/meilisearch-graphql-typings/meiliTypes"; import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search"; import { Paginator } from "components/Containers/Paginator"; import { useFormat } from "hooks/useFormat"; import { getFormat } from "helpers/i18n"; import { useAtomSetter } from "helpers/atoms"; import { atoms } from "contexts/atoms"; /* * ╭─────────────╮ * ────────────────────────────────────────╯ CONSTANTS ╰────────────────────────────────────────── */ const DEFAULT_FILTERS_STATE = { query: "", keepInfoVisible: true, page: 1, }; const queryParamSchema = z.object({ query: z.coerce.string().optional(), page: z.coerce.number().positive().optional(), }); /* * ╭────────╮ * ──────────────────────────────────────────╯ PAGE ╰───────────────────────────────────────────── */ interface Props extends AppLayoutRequired {} const Wiki = (props: Props): JSX.Element => { const { format, formatCategory, formatWikiTag } = useFormat(); const hoverable = useDeviceSupportsHover(); const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened); const closeSubPanel = useCallback(() => setSubPanelOpened(false), [setSubPanelOpened]); const router = useTypedRouter(queryParamSchema); const [query, setQuery] = useState(router.query.query ?? DEFAULT_FILTERS_STATE.query); const { value: keepInfoVisible, toggle: toggleKeepInfoVisible, setValue: setKeepInfoVisible, } = useBoolean(DEFAULT_FILTERS_STATE.keepInfoVisible); const [page, setPage] = useState(router.query.page ?? DEFAULT_FILTERS_STATE.page); const [wikiPages, setWikiPages] = useState>(); useEffect(() => { const fetchWikiPages = async () => { const searchResult = await meiliSearch(MeiliIndices.WIKI_PAGE, query, { hitsPerPage: 25, page, attributesToHighlight: [ "translations.title", "translations.aliases", "translations.summary", "translations.displayable_description", ], attributesToCrop: ["translations.displayable_description"], }); setWikiPages(searchResult); }; fetchWikiPages(); }, [query, page]); useEffect(() => { if (router.isReady) router.updateQuery({ page, query, }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [page, query, router.isReady]); useEffect(() => { if (router.isReady) { if (isDefined(router.query.page)) setPage(router.query.page); if (isDefined(router.query.query)) setQuery(router.query.query); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [router.isReady]); const subPanel = ( { setPage(1); setQuery(name); if (isDefinedAndNotEmpty(name)) { sendAnalytics("Wiki", "Change search term"); } else { sendAnalytics("Wiki", "Clear search term"); } }} /> {hoverable && ( { toggleKeepInfoVisible(); sendAnalytics("Wiki", `Always ${keepInfoVisible ? "hide" : "show"} info`); }} /> )}