2022-07-23 10:24:13 +02:00

341 lines
11 KiB
TypeScript

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",
};
};