import { GetStaticProps } from "next"; import { useEffect, useState } from "react"; import { useBoolean } from "usehooks-ts"; import { z } from "zod"; import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { Switch } from "components/Inputs/Switch"; import { PanelHeader } from "components/PanelComponents/PanelHeader"; import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel"; import { SubPanel } from "components/Containers/SubPanel"; import { WithLabel } from "components/Inputs/WithLabel"; import { TextInput } from "components/Inputs/TextInput"; import { Button } from "components/Inputs/Button"; import { useDeviceSupportsHover } from "hooks/useMediaQuery"; import { filterDefined, filterHasAttributes, isDefined, isDefinedAndNotEmpty, } from "helpers/asserts"; import { getOpenGraph } from "helpers/openGraph"; import { TranslatedPreviewCard } from "components/PreviewCard"; import { HorizontalLine } from "components/HorizontalLine"; import { sendAnalytics } from "helpers/analytics"; import { Terminal } from "components/Cli/Terminal"; import { atoms } from "contexts/atoms"; import { useAtomGetter } from "helpers/atoms"; import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search"; import { MeiliIndices, MeiliPost } from "shared/meilisearch-graphql-typings/meiliTypes"; import { useTypedRouter } from "hooks/useTypedRouter"; import { prettySlug } from "helpers/formatters"; import { Paginator } from "components/Containers/Paginator"; import { useFormat } from "hooks/useFormat"; import { getFormat } from "helpers/i18n"; /* * ╭─────────────╮ * ────────────────────────────────────────╯ 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 News = ({ ...otherProps }: Props): JSX.Element => { const { format } = useFormat(); const hoverable = useDeviceSupportsHover(); 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 isTerminalMode = useAtomGetter(atoms.layout.terminalMode); const [page, setPage] = useState(router.query.page ?? DEFAULT_FILTERS_STATE.page); const [posts, setPosts] = useState>(); useEffect(() => { const fetchPosts = async () => { const searchResult = await meiliSearch(MeiliIndices.POST, query, { hitsPerPage: 25, page, attributesToRetrieve: ["translations", "thumbnail", "slug", "date", "categories"], attributesToHighlight: ["translations.title", "translations.excerpt", "translations.body"], attributesToCrop: ["translations.body"], sort: ["sortable_date:desc"], filter: ["hidden = false"], }); searchResult.hits = searchResult.hits.map((item) => { if (Object.keys(item._matchesPosition).some((match) => match.startsWith("translations"))) { item._formatted.translations = filterDefined(item._formatted.translations).filter( (translation) => JSON.stringify(translation).includes("") ); } return item; }); setPosts(searchResult); }; fetchPosts(); }, [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 = ( { setQuery(name); if (isDefinedAndNotEmpty(name)) { sendAnalytics("News", "Change search term"); } else { sendAnalytics("News", "Clear search term"); } }} /> {hoverable && ( { toggleKeepInfoVisible(); sendAnalytics("News", `Always ${keepInfoVisible ? "hide" : "show"} info`); }} /> )}