247 lines
9.6 KiB
TypeScript
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",
|
|
};
|
|
};
|