2023-05-03 03:49:14 +02:00

247 lines
9.6 KiB
TypeScript

import { GetStaticProps, GetStaticPaths, GetStaticPathsResult } from "next";
import { useCallback } from "react";
import { getReadySdk } from "graphql/sdk";
import { isDefined, filterHasAttributes } from "helpers/asserts";
import { ChronicleWithTranslations } from "types/types";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { useSmartLanguage } from "hooks/useSmartLanguage";
import { ContentPanel } from "components/Containers/ContentPanel";
import { Markdawn } from "components/Markdown/Markdawn";
import { SubPanel } from "components/Containers/SubPanel";
import { ThumbnailHeader } from "components/ThumbnailHeader";
import { HorizontalLine } from "components/HorizontalLine";
import { GetChroniclesChaptersQuery } from "graphql/generated";
import { prettyInlineTitle, prettySlug } from "helpers/formatters";
import { ReturnButton } from "components/PanelComponents/ReturnButton";
import { getOpenGraph } from "helpers/openGraph";
import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/locales";
import { getDescription } from "helpers/description";
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
import { Ids } from "types/ids";
import { useFormat } from "hooks/useFormat";
import { getFormat } from "helpers/i18n";
import { ElementsSeparator } from "helpers/component";
import { ChroniclesLists } from "components/Chronicles/ChroniclesLists";
/*
* ╭────────╮
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
*/
interface Props extends AppLayoutRequired {
chronicle: ChronicleWithTranslations;
chapters: NonNullable<GetChroniclesChaptersQuery["chroniclesChapters"]>["data"];
}
const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element => {
const { format } = useFormat();
useScrollTopOnChange(Ids.ContentPanel, [chronicle.slug]);
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
items: chronicle.translations,
languageExtractor: useCallback(
(item: ChronicleWithTranslations["translations"][number]) =>
item?.language?.data?.attributes?.code,
[]
),
});
const primaryContent = chronicle.contents
? filterHasAttributes(chronicle.contents.data, ["attributes.translations"])[0]?.attributes
: undefined;
const [selectedContentTranslation, ContentLanguageSwitcher, ContentLanguageSwitcherProps] =
useSmartLanguage({
items: primaryContent?.translations ?? [],
languageExtractor: useCallback(
(
item: NonNullable<
NonNullable<
NonNullable<ChronicleWithTranslations["contents"]>["data"][number]["attributes"]
>["translations"]
>[number]
) => item?.language?.data?.attributes?.code,
[]
),
});
const subPanel = (
<SubPanel>
<ReturnButton
displayOnlyOn={"3ColumnsLayout"}
href="/chronicles"
title={format("chronicles")}
/>
<HorizontalLine />
<ChroniclesLists chapters={chapters} currentChronicleSlug={chronicle.slug} />
</SubPanel>
);
const contentPanel = (
<ContentPanel>
<ReturnButton
displayOnlyOn={"1ColumnLayout"}
href="/chronicles"
title={format("chronicles")}
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 && (
<ElementsSeparator>
{[
<ThumbnailHeader
key="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}
/>,
selectedContentTranslation.text_set?.text && (
<Markdawn text={selectedContentTranslation.text_set.text} />
),
]}
</ElementsSeparator>
)}
</>
)}
</ContentPanel>
);
return (
<AppLayout
contentPanel={contentPanel}
subPanel={subPanel}
subPanelIcon="format_list_numbered"
{...otherProps}
/>
);
};
export default Chronicle;
/*
* ╭──────────────────────╮
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
*/
export const getStaticProps: GetStaticProps = async (context) => {
const sdk = getReadySdk();
const { format } = getFormat(context.locale);
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 { 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, {
[format("type", { count: Infinity })]: [
chronicle.chronicles.data[0].attributes.contents.data[0].attributes.type?.data
?.attributes?.titles?.[0]?.title,
],
[format("category", { count: Infinity })]: filterHasAttributes(
chronicle.chronicles.data[0].attributes.contents.data[0].attributes.categories
?.data,
["attributes"]
).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 = {
chronicle: chronicle.chronicles.data[0].attributes as ChronicleWithTranslations,
chapters: chronicles.chroniclesChapters.data,
openGraph: getOpenGraph(format, 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"]).map((wikiPage) => {
context.locales?.map((local) =>
paths.push({
params: { slug: wikiPage.attributes.slug },
locale: local,
})
);
});
return {
paths,
fallback: "blocking",
};
};