import { GetStaticProps, GetStaticPaths, GetStaticPathsResult } from "next"; import { useCallback, useMemo } from "react"; import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps"; import { getReadySdk } from "graphql/sdk"; import { isDefined, filterHasAttributes } from "helpers/others"; import { ChronicleWithTranslations } from "helpers/types"; import { AppLayout, AppLayoutRequired } from "components/AppLayout"; import { useSmartLanguage } from "hooks/useSmartLanguage"; import { ContentPanel } from "components/Panels/ContentPanel"; import { Markdawn } from "components/Markdown/Markdawn"; import { SubPanel } from "components/Panels/SubPanel"; import { ThumbnailHeader } from "components/ThumbnailHeader"; import { HorizontalLine } from "components/HorizontalLine"; import { GetChroniclesChaptersQuery } from "graphql/generated"; import { prettyInlineTitle, prettySlug } from "helpers/formatters"; import { ReturnButton, ReturnButtonType, } from "components/PanelComponents/ReturnButton"; import { TranslatedChroniclesList } from "components/Translated"; import { Icon } from "components/Ico"; import { getOpenGraph } from "helpers/openGraph"; import { getDefaultPreferredLanguages, staticSmartLanguage, } from "helpers/locales"; import { getDescription } from "helpers/description"; /* * ╭────────╮ * ──────────────────────────────────────────╯ PAGE ╰───────────────────────────────────────────── */ interface Props extends AppStaticProps, AppLayoutRequired { chronicle: ChronicleWithTranslations; chapters: NonNullable< GetChroniclesChaptersQuery["chroniclesChapters"] >["data"]; } const Chronicle = ({ chronicle, chapters, langui, languages, ...otherProps }: Props): JSX.Element => { const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({ items: chronicle.translations, languages: languages, languageExtractor: useCallback( (item: ChronicleWithTranslations["translations"][number]) => item?.language?.data?.attributes?.code, [] ), }); const primaryContent = useMemo< NonNullable< ChronicleWithTranslations["contents"] >["data"][number]["attributes"] >( () => filterHasAttributes(chronicle.contents?.data, [ "attributes.translations", ] as const)[0]?.attributes, [chronicle.contents?.data] ); const [ selectedContentTranslation, ContentLanguageSwitcher, ContentLanguageSwitcherProps, ] = useSmartLanguage({ items: primaryContent?.translations ?? [], languages: languages, languageExtractor: useCallback( ( item: NonNullable< NonNullable< NonNullable< ChronicleWithTranslations["contents"] >["data"][number]["attributes"] >["translations"] >[number] ) => item?.language?.data?.attributes?.code, [] ), }); const contentPanel = useMemo( () => ( <ContentPanel> <ReturnButton displayOn={ReturnButtonType.Mobile} href="/chronicles" title={langui.chronicles} langui={langui} className="mb-10" /> {isDefined(selectedTranslation) ? ( <> <h1 className="mb-16 text-center text-3xl"> {selectedTranslation.title} </h1> {languageSwitcherProps.locales.size > 1 && ( <LanguageSwitcher {...languageSwitcherProps} /> )} {isDefined(selectedTranslation.body) && ( <Markdawn text={selectedTranslation.body.body} /> )} </> ) : ( <> {selectedContentTranslation && ( <> <ThumbnailHeader pre_title={selectedContentTranslation.pre_title} title={selectedContentTranslation.title} subtitle={selectedContentTranslation.subtitle} languageSwitcher={ ContentLanguageSwitcherProps.locales.size > 1 ? ( <ContentLanguageSwitcher {...ContentLanguageSwitcherProps} /> ) : undefined } categories={primaryContent?.categories} type={primaryContent?.type} description={selectedContentTranslation.description} thumbnail={primaryContent?.thumbnail?.data?.attributes} langui={langui} /> <HorizontalLine /> {selectedContentTranslation.text_set?.text && ( <Markdawn text={selectedContentTranslation.text_set.text} /> )} </> )} </> )} </ContentPanel> ), [ selectedTranslation, languageSwitcherProps, LanguageSwitcher, selectedContentTranslation, ContentLanguageSwitcherProps, ContentLanguageSwitcher, primaryContent?.categories, primaryContent?.type, primaryContent?.thumbnail?.data?.attributes, langui, ] ); const subPanel = useMemo( () => ( <SubPanel> <ReturnButton displayOn={ReturnButtonType.Desktop} href="/chronicles" title={langui.chronicles} langui={langui} horizontalLine /> <div className="grid gap-16"> {filterHasAttributes(chapters, [ "attributes.chronicles", "id", ] as const).map((chapter) => ( <TranslatedChroniclesList key={chapter.id} chronicles={chapter.attributes.chronicles.data} translations={filterHasAttributes(chapter.attributes.titles, [ "language.data.attributes.code", ] as const).map((translation) => ({ title: translation.title, language: translation.language.data.attributes.code, }))} fallback={{ title: prettySlug(chapter.attributes.slug) }} currentSlug={chronicle.slug} /> ))} </div> </SubPanel> ), [chapters, chronicle.slug, langui] ); return ( <AppLayout contentPanel={contentPanel} subPanel={subPanel} langui={langui} languages={languages} subPanelIcon={Icon.FormatListNumbered} {...otherProps} /> ); }; export default Chronicle; /* * ╭──────────────────────╮ * ───────────────────────────────────╯ NEXT DATA FETCHING ╰────────────────────────────────────── */ export const getStaticProps: GetStaticProps = async (context) => { const sdk = getReadySdk(); const slug = context.params && isDefined(context.params.slug) ? context.params.slug.toString() : ""; const chronicle = await sdk.getChronicle({ language_code: context.locale ?? "en", slug: slug, }); const chronicles = await sdk.getChroniclesChapters(); if ( !chronicle.chronicles?.data[0]?.attributes?.translations || !chronicles.chroniclesChapters?.data ) return { notFound: true }; const appStaticProps = await getAppStaticProps(context); const { title, description } = (() => { if (context.locale && context.locales) { if ( chronicle.chronicles.data[0].attributes.contents?.data[0]?.attributes ?.translations ) { const selectedContentTranslation = staticSmartLanguage({ items: chronicle.chronicles.data[0].attributes.contents.data[0].attributes .translations, languageExtractor: (item) => item.language?.data?.attributes?.code, preferredLanguages: getDefaultPreferredLanguages( context.locale, context.locales ), }); if (selectedContentTranslation) { return { title: prettyInlineTitle( selectedContentTranslation.pre_title, selectedContentTranslation.title, selectedContentTranslation.subtitle ), description: getDescription( selectedContentTranslation.description, { [appStaticProps.langui.type ?? "Type"]: [ chronicle.chronicles.data[0].attributes.contents.data[0] .attributes.type?.data?.attributes?.titles?.[0]?.title, ], [appStaticProps.langui.categories ?? "Categories"]: filterHasAttributes( chronicle.chronicles.data[0].attributes.contents.data[0] .attributes.categories?.data, ["attributes"] as const ).map((category) => category.attributes.short), } ), }; } } else { const selectedTranslation = staticSmartLanguage({ items: chronicle.chronicles.data[0].attributes.translations, languageExtractor: (item) => item.language?.data?.attributes?.code, preferredLanguages: getDefaultPreferredLanguages( context.locale, context.locales ), }); if (selectedTranslation) { return { title: selectedTranslation.title, description: selectedTranslation.summary, }; } } } return { title: prettySlug(chronicle.chronicles.data[0].attributes.slug), description: undefined, }; })(); const thumbnail = chronicle.chronicles.data[0].attributes.translations.length === 0 ? chronicle.chronicles.data[0].attributes.contents?.data[0]?.attributes ?.thumbnail?.data?.attributes : undefined; const props: Props = { ...appStaticProps, chronicle: chronicle.chronicles.data[0] .attributes as ChronicleWithTranslations, chapters: chronicles.chroniclesChapters.data, openGraph: getOpenGraph( appStaticProps.langui, title, description, thumbnail ), }; return { props: props, }; }; // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ export const getStaticPaths: GetStaticPaths = async (context) => { const sdk = getReadySdk(); const contents = await sdk.getChroniclesSlugs(); const paths: GetStaticPathsResult["paths"] = []; filterHasAttributes(contents.chronicles?.data, ["attributes"] as const).map( (wikiPage) => { context.locales?.map((local) => paths.push({ params: { slug: wikiPage.attributes.slug }, locale: local, }) ); } ); return { paths, fallback: "blocking", }; };