353 lines
12 KiB
TypeScript
353 lines
12 KiB
TypeScript
import AppLayout from "components/AppLayout";
|
|
import Button from "components/Button";
|
|
import Chip from "components/Chip";
|
|
import HorizontalLine from "components/HorizontalLine";
|
|
import LanguageSwitcher from "components/LanguageSwitcher";
|
|
import Markdawn from "components/Markdown/Markdawn";
|
|
import TOC from "components/Markdown/TOC";
|
|
import ReturnButton, {
|
|
ReturnButtonType,
|
|
} from "components/PanelComponents/ReturnButton";
|
|
import ContentPanel from "components/Panels/ContentPanel";
|
|
import SubPanel from "components/Panels/SubPanel";
|
|
import RecorderChip from "components/RecorderChip";
|
|
import ThumbnailHeader from "components/ThumbnailHeader";
|
|
import ToolTip from "components/ToolTip";
|
|
import { useAppLayout } from "contexts/AppLayoutContext";
|
|
import { GetContentTextQuery } from "graphql/generated";
|
|
import { getReadySdk } from "graphql/sdk";
|
|
import {
|
|
GetStaticPathsContext,
|
|
GetStaticPathsResult,
|
|
GetStaticPropsContext,
|
|
} from "next";
|
|
import { useRouter } from "next/router";
|
|
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
|
|
import {
|
|
getPreferredLanguage,
|
|
getStatusDescription,
|
|
prettyinlineTitle,
|
|
prettyLanguage,
|
|
prettySlug,
|
|
} from "queries/helpers";
|
|
import { useEffect, useMemo, useState } from "react";
|
|
|
|
interface Props extends AppStaticProps {
|
|
content: Exclude<
|
|
GetContentTextQuery["contents"],
|
|
null | undefined
|
|
>["data"][number]["attributes"];
|
|
contentId: Exclude<
|
|
GetContentTextQuery["contents"],
|
|
null | undefined
|
|
>["data"][number]["id"];
|
|
}
|
|
|
|
export default function Content(props: Props): JSX.Element {
|
|
const { langui, content, languages } = props;
|
|
const router = useRouter();
|
|
const appLayout = useAppLayout();
|
|
|
|
const [selectedTextSet, setSelectedTextSet] = useState<
|
|
| Exclude<
|
|
Exclude<Props["content"], null | undefined>["text_set"],
|
|
null | undefined
|
|
>[number]
|
|
>();
|
|
const [selectedTitle, setSelectedTitle] = useState<
|
|
| Exclude<
|
|
Exclude<Props["content"], null | undefined>["titles"],
|
|
null | undefined
|
|
>[number]
|
|
>();
|
|
const textSetLocales: Map<string, number> = new Map();
|
|
|
|
const [selectedTextSetIndex, setSelectedTextSetIndex] = useState<
|
|
number | undefined
|
|
>();
|
|
|
|
if (content?.text_set) {
|
|
content.text_set.map((textSet, index) => {
|
|
if (textSet?.language?.data?.attributes?.code && textSet.text) {
|
|
textSetLocales.set(textSet.language.data.attributes.code, index);
|
|
}
|
|
});
|
|
}
|
|
|
|
useMemo(() => {
|
|
setSelectedTextSetIndex(
|
|
getPreferredLanguage(
|
|
appLayout.preferredLanguages ?? [router.locale],
|
|
textSetLocales
|
|
)
|
|
);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [appLayout.preferredLanguages]);
|
|
|
|
useEffect(() => {
|
|
if (selectedTextSetIndex !== undefined)
|
|
setSelectedTextSet(content?.text_set?.[selectedTextSetIndex]);
|
|
if (selectedTextSetIndex !== undefined)
|
|
setSelectedTitle(content?.titles?.[selectedTextSetIndex]);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [selectedTextSetIndex]);
|
|
|
|
const subPanel = (
|
|
<SubPanel>
|
|
<ReturnButton
|
|
href={`/contents`}
|
|
title={langui.contents}
|
|
langui={langui}
|
|
displayOn={ReturnButtonType.desktop}
|
|
horizontalLine
|
|
/>
|
|
|
|
{selectedTextSet?.source_language?.data?.attributes && (
|
|
<div className="grid gap-5">
|
|
<h2 className="text-xl">
|
|
{selectedTextSet.source_language.data.attributes.code ===
|
|
selectedTextSet.language?.data?.attributes?.code
|
|
? langui.transcript_notice
|
|
: langui.translation_notice}
|
|
</h2>
|
|
|
|
{selectedTextSet.source_language.data.attributes.code !==
|
|
selectedTextSet.language?.data?.attributes?.code && (
|
|
<div className="grid place-items-center gap-2">
|
|
<p className="font-headers">{langui.source_language}:</p>
|
|
<Button
|
|
href={router.asPath}
|
|
locale={selectedTextSet.source_language.data.attributes.code}
|
|
>
|
|
{prettyLanguage(
|
|
selectedTextSet.source_language.data.attributes.code,
|
|
languages
|
|
)}
|
|
</Button>
|
|
</div>
|
|
)}
|
|
|
|
<div className="grid grid-flow-col place-items-center place-content-center gap-2">
|
|
<p className="font-headers">{langui.status}:</p>
|
|
|
|
<ToolTip
|
|
content={getStatusDescription(selectedTextSet.status, langui)}
|
|
maxWidth={"20rem"}
|
|
>
|
|
<Chip>{selectedTextSet.status}</Chip>
|
|
</ToolTip>
|
|
</div>
|
|
|
|
{selectedTextSet.transcribers &&
|
|
selectedTextSet.transcribers.data.length > 0 && (
|
|
<div>
|
|
<p className="font-headers">{langui.transcribers}:</p>
|
|
<div className="grid place-items-center place-content-center gap-2">
|
|
{selectedTextSet.transcribers.data.map((recorder) => (
|
|
<>
|
|
{recorder.attributes && (
|
|
<RecorderChip
|
|
key={recorder.id}
|
|
langui={langui}
|
|
recorder={recorder.attributes}
|
|
/>
|
|
)}
|
|
</>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{selectedTextSet.translators &&
|
|
selectedTextSet.translators.data.length > 0 && (
|
|
<div>
|
|
<p className="font-headers">{langui.translators}:</p>
|
|
<div className="grid place-items-center place-content-center gap-2">
|
|
{selectedTextSet.translators.data.map((recorder) => (
|
|
<>
|
|
{recorder.attributes && (
|
|
<RecorderChip
|
|
key={recorder.id}
|
|
langui={langui}
|
|
recorder={recorder.attributes}
|
|
/>
|
|
)}
|
|
</>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{selectedTextSet.proofreaders &&
|
|
selectedTextSet.proofreaders.data.length > 0 && (
|
|
<div>
|
|
<p className="font-headers">{langui.proofreaders}:</p>
|
|
<div className="grid place-items-center place-content-center gap-2">
|
|
{selectedTextSet.proofreaders.data.map((recorder) => (
|
|
<>
|
|
{recorder.attributes && (
|
|
<RecorderChip
|
|
key={recorder.id}
|
|
langui={langui}
|
|
recorder={recorder.attributes}
|
|
/>
|
|
)}
|
|
</>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{selectedTextSet.notes && (
|
|
<div>
|
|
<p className="font-headers">{"Notes"}:</p>
|
|
<div className="grid place-items-center place-content-center gap-2">
|
|
<Markdawn text={selectedTextSet.notes} />
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{selectedTextSet && content?.text_set && selectedTextSet.text && (
|
|
<>
|
|
<HorizontalLine />
|
|
<TOC
|
|
text={selectedTextSet.text}
|
|
title={
|
|
content.titles && content.titles.length > 0 && selectedTitle
|
|
? prettyinlineTitle(
|
|
selectedTitle.pre_title,
|
|
selectedTitle.title,
|
|
selectedTitle.subtitle
|
|
)
|
|
: prettySlug(content.slug)
|
|
}
|
|
/>
|
|
</>
|
|
)}
|
|
</SubPanel>
|
|
);
|
|
const contentPanel = (
|
|
<ContentPanel>
|
|
<ReturnButton
|
|
href={`/contents/${content?.slug}`}
|
|
title={langui.content}
|
|
langui={langui}
|
|
displayOn={ReturnButtonType.mobile}
|
|
className="mb-10"
|
|
/>
|
|
|
|
{content && (
|
|
<div className="grid place-items-center">
|
|
<ThumbnailHeader
|
|
thumbnail={content.thumbnail?.data?.attributes}
|
|
pre_title={
|
|
selectedTitle?.pre_title ?? content.titles?.[0]?.pre_title
|
|
}
|
|
title={selectedTitle?.title ?? content.titles?.[0]?.title}
|
|
subtitle={selectedTitle?.subtitle ?? content.titles?.[0]?.subtitle}
|
|
description={
|
|
selectedTitle?.description ?? content.titles?.[0]?.description
|
|
}
|
|
type={content.type}
|
|
categories={content.categories}
|
|
langui={langui}
|
|
languageSwitcher={
|
|
selectedTextSet ? (
|
|
<LanguageSwitcher
|
|
locales={textSetLocales}
|
|
languages={props.languages}
|
|
localesIndex={selectedTextSetIndex}
|
|
setLocalesIndex={setSelectedTextSetIndex}
|
|
/>
|
|
) : undefined
|
|
}
|
|
/>
|
|
|
|
<HorizontalLine />
|
|
|
|
<Markdawn text={selectedTextSet?.text ?? ""} />
|
|
</div>
|
|
)}
|
|
</ContentPanel>
|
|
);
|
|
|
|
let description = "";
|
|
if (content?.type?.data) {
|
|
description += `${langui.type}: `;
|
|
|
|
description +=
|
|
content.type.data.attributes?.titles?.[0]?.title ??
|
|
prettySlug(content.type.data.attributes?.slug);
|
|
|
|
description += "\n";
|
|
}
|
|
if (content?.categories?.data && content.categories.data.length > 0) {
|
|
description += `${langui.categories}: `;
|
|
description += content.categories.data
|
|
.map((category) => category.attributes?.short)
|
|
.join(" | ");
|
|
description += "\n";
|
|
}
|
|
|
|
return (
|
|
<AppLayout
|
|
navTitle={
|
|
content?.titles && content.titles.length > 0 && content.titles[0]
|
|
? prettyinlineTitle(
|
|
content.titles[0].pre_title,
|
|
content.titles[0].title,
|
|
content.titles[0].subtitle
|
|
)
|
|
: prettySlug(content?.slug)
|
|
}
|
|
thumbnail={content?.thumbnail?.data?.attributes ?? undefined}
|
|
contentPanel={contentPanel}
|
|
subPanel={subPanel}
|
|
description={description}
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
|
|
export async function getStaticProps(
|
|
context: GetStaticPropsContext
|
|
): Promise<{ notFound: boolean } | { props: Props }> {
|
|
const sdk = getReadySdk();
|
|
const slug = context.params?.slug ? context.params.slug.toString() : "";
|
|
const content = await sdk.getContentText({
|
|
slug: slug,
|
|
language_code: context.locale ?? "en",
|
|
});
|
|
|
|
if (!content.contents || content.contents.data.length === 0)
|
|
return { notFound: true };
|
|
const props: Props = {
|
|
...(await getAppStaticProps(context)),
|
|
content: content.contents.data[0].attributes,
|
|
contentId: content.contents.data[0].id,
|
|
};
|
|
return {
|
|
props: props,
|
|
};
|
|
}
|
|
|
|
export async function getStaticPaths(
|
|
context: GetStaticPathsContext
|
|
): Promise<GetStaticPathsResult> {
|
|
const sdk = getReadySdk();
|
|
const contents = await sdk.getContentsSlugs();
|
|
const paths: GetStaticPathsResult["paths"] = [];
|
|
contents.contents?.data.map((item) => {
|
|
context.locales?.map((local) => {
|
|
if (item.attributes)
|
|
paths.push({ params: { slug: item.attributes.slug }, locale: local });
|
|
});
|
|
});
|
|
return {
|
|
paths,
|
|
fallback: "blocking",
|
|
};
|
|
}
|