407 lines
14 KiB
TypeScript
407 lines
14 KiB
TypeScript
import { AppLayout } from "components/AppLayout";
|
|
import { Chip } from "components/Chip";
|
|
import { HorizontalLine } from "components/HorizontalLine";
|
|
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 { PreviewLine } from "components/PreviewLine";
|
|
import { RecorderChip } from "components/RecorderChip";
|
|
import { ThumbnailHeader } from "components/ThumbnailHeader";
|
|
import { ToolTip } from "components/ToolTip";
|
|
import { GetContentTextQuery } from "graphql/generated";
|
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
|
import { getReadySdk } from "graphql/sdk";
|
|
import {
|
|
prettyinlineTitle,
|
|
prettyLanguage,
|
|
prettySlug,
|
|
} from "helpers/formatters";
|
|
import { getStatusDescription } from "helpers/others";
|
|
import { Immutable } from "helpers/types";
|
|
import { useMediaMobile } from "hooks/useMediaQuery";
|
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
|
import {
|
|
GetStaticPathsContext,
|
|
GetStaticPathsResult,
|
|
GetStaticPropsContext,
|
|
} from "next";
|
|
|
|
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: Immutable<Props>): JSX.Element {
|
|
const { langui, content, languages } = props;
|
|
const isMobile = useMediaMobile();
|
|
|
|
const [selectedTextSet, LanguageSwitcher] = useSmartLanguage({
|
|
items: content?.text_set,
|
|
languages: languages,
|
|
languageExtractor: (item) => item?.language?.data?.attributes?.code,
|
|
});
|
|
|
|
const selectedTitle = content?.titles?.[0];
|
|
|
|
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>
|
|
<Chip>
|
|
{prettyLanguage(
|
|
selectedTextSet.source_language.data.attributes.code,
|
|
languages
|
|
)}
|
|
</Chip>
|
|
</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={<LanguageSwitcher />}
|
|
/>
|
|
|
|
{content.previous_recommended?.data?.attributes && (
|
|
<div className="mt-12 mb-8 w-full">
|
|
<h2 className="text-center text-2xl mb-4">Previous content</h2>
|
|
<PreviewLine
|
|
href={`/contents/${content.previous_recommended.data.attributes.slug}`}
|
|
pre_title={
|
|
content.previous_recommended.data.attributes.titles?.[0]
|
|
?.pre_title
|
|
}
|
|
title={
|
|
content.previous_recommended.data.attributes.titles?.[0]
|
|
?.title ??
|
|
prettySlug(content.previous_recommended.data.attributes.slug)
|
|
}
|
|
subtitle={
|
|
content.previous_recommended.data.attributes.titles?.[0]
|
|
?.subtitle
|
|
}
|
|
thumbnail={
|
|
content.previous_recommended.data.attributes.thumbnail?.data
|
|
?.attributes
|
|
}
|
|
thumbnailAspectRatio="3/2"
|
|
topChips={
|
|
isMobile
|
|
? undefined
|
|
: content.previous_recommended.data.attributes.type?.data
|
|
?.attributes
|
|
? [
|
|
content.previous_recommended.data.attributes.type.data
|
|
.attributes.titles?.[0]
|
|
? content.previous_recommended.data.attributes.type
|
|
.data.attributes.titles[0]?.title
|
|
: prettySlug(
|
|
content.previous_recommended.data.attributes.type
|
|
.data.attributes.slug
|
|
),
|
|
]
|
|
: undefined
|
|
}
|
|
bottomChips={
|
|
isMobile
|
|
? undefined
|
|
: content.previous_recommended.data.attributes.categories?.data.map(
|
|
(category) => category.attributes?.short ?? ""
|
|
)
|
|
}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
<HorizontalLine />
|
|
|
|
<Markdawn text={selectedTextSet?.text ?? ""} />
|
|
|
|
{content.next_recommended?.data?.attributes && (
|
|
<>
|
|
<HorizontalLine />
|
|
<h2 className="text-center text-2xl mb-4">Follow-up content</h2>
|
|
<PreviewLine
|
|
href={`/contents/${content.next_recommended.data.attributes.slug}`}
|
|
pre_title={
|
|
content.next_recommended.data.attributes.titles?.[0]
|
|
?.pre_title
|
|
}
|
|
title={
|
|
content.next_recommended.data.attributes.titles?.[0]?.title ??
|
|
prettySlug(content.next_recommended.data.attributes.slug)
|
|
}
|
|
subtitle={
|
|
content.next_recommended.data.attributes.titles?.[0]?.subtitle
|
|
}
|
|
thumbnail={
|
|
content.next_recommended.data.attributes.thumbnail?.data
|
|
?.attributes
|
|
}
|
|
thumbnailAspectRatio="3/2"
|
|
topChips={
|
|
isMobile
|
|
? undefined
|
|
: content.next_recommended.data.attributes.type?.data
|
|
?.attributes
|
|
? [
|
|
content.next_recommended.data.attributes.type.data
|
|
.attributes.titles?.[0]
|
|
? content.next_recommended.data.attributes.type.data
|
|
.attributes.titles[0]?.title
|
|
: prettySlug(
|
|
content.next_recommended.data.attributes.type.data
|
|
.attributes.slug
|
|
),
|
|
]
|
|
: undefined
|
|
}
|
|
bottomChips={
|
|
isMobile
|
|
? undefined
|
|
: content.next_recommended.data.attributes.categories?.data.map(
|
|
(category) => category.attributes?.short ?? ""
|
|
)
|
|
}
|
|
/>
|
|
</>
|
|
)}
|
|
</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",
|
|
};
|
|
}
|