import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next"; import { Fragment, useCallback, useMemo } from "react"; import { AppLayout } from "components/AppLayout"; import { Chip } from "components/Chip"; import { HorizontalLine } from "components/HorizontalLine"; import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs"; import { Markdawn, TableOfContents } from "components/Markdown/Markdawn"; import { ReturnButton, ReturnButtonType, } from "components/PanelComponents/ReturnButton"; import { ContentPanel } from "components/Panels/ContentPanel"; import { SubPanel } from "components/Panels/SubPanel"; import { PreviewCard } from "components/PreviewCard"; import { RecorderChip } from "components/RecorderChip"; import { ThumbnailHeader } from "components/ThumbnailHeader"; import { ToolTip } from "components/ToolTip"; import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps"; import { getReadySdk } from "graphql/sdk"; import { getDescription } from "helpers/description"; import { prettyinlineTitle, prettyLanguage, prettyItemSubType, prettySlug, } from "helpers/formatters"; import { isUntangibleGroupItem } from "helpers/libraryItem"; import { filterHasAttributes, getStatusDescription, isDefinedAndNotEmpty, } from "helpers/others"; import { ContentWithTranslations } from "helpers/types"; import { useMediaMobile } from "hooks/useMediaQuery"; import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange"; import { useSmartLanguage } from "hooks/useSmartLanguage"; import { TranslatedPreviewLine } from "components/Translated"; /* * ╭────────╮ * ──────────────────────────────────────────╯ PAGE ╰───────────────────────────────────────────── */ interface Props extends AppStaticProps { content: ContentWithTranslations; } const Content = ({ langui, content, languages, currencies, ...otherProps }: Props): JSX.Element => { const isMobile = useMediaMobile(); const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({ items: content.translations, languages: languages, languageExtractor: useCallback( (item: NonNullable) => item.language?.data?.attributes?.code, [] ), }); useScrollTopOnChange(AnchorIds.ContentPanel, [selectedTranslation]); const { previousContent, nextContent } = useMemo( () => ({ previousContent: content.group?.data?.attributes?.contents ? getPreviousContent( content.group.data.attributes.contents.data, content.slug ) : undefined, nextContent: content.group?.data?.attributes?.contents ? getNextContent( content.group.data.attributes.contents.data, content.slug ) : undefined, }), [content.group, content.slug] ); const subPanel = useMemo( () => ( {selectedTranslation?.text_set?.source_language?.data?.attributes ?.code !== undefined && (

{selectedTranslation.text_set.source_language.data.attributes .code === selectedTranslation.language?.data?.attributes?.code ? langui.transcript_notice : langui.translation_notice}

{selectedTranslation.text_set.source_language.data.attributes .code !== selectedTranslation.language?.data?.attributes?.code && (

{langui.source_language}:

)}

{langui.status}:

{selectedTranslation.text_set.transcribers && selectedTranslation.text_set.transcribers.data.length > 0 && (

{langui.transcribers}:

{filterHasAttributes( selectedTranslation.text_set.transcribers.data, ["attributes", "id"] as const ).map((recorder) => ( ))}
)} {selectedTranslation.text_set.translators && selectedTranslation.text_set.translators.data.length > 0 && (

{langui.translators}:

{filterHasAttributes( selectedTranslation.text_set.translators.data, ["attributes", "id"] as const ).map((recorder) => ( ))}
)} {selectedTranslation.text_set.proofreaders && selectedTranslation.text_set.proofreaders.data.length > 0 && (

{langui.proofreaders}:

{filterHasAttributes( selectedTranslation.text_set.proofreaders.data, ["attributes", "id"] as const ).map((recorder) => ( ))}
)} {isDefinedAndNotEmpty(selectedTranslation.text_set.notes) && (

{langui.notes}:

)}
)} {content.ranged_contents?.data && content.ranged_contents.data.length > 0 && ( <>

{langui.source}

{filterHasAttributes(content.ranged_contents.data, [ "attributes.library_item.data.attributes", "attributes.library_item.data.id", ] as const).map((rangedContent) => { const libraryItem = rangedContent.attributes.library_item.data; return (
0 && libraryItem.attributes.metadata[0] ? [ prettyItemSubType( libraryItem.attributes.metadata[0] ), ] : [] } bottomChips={filterHasAttributes( libraryItem.attributes.categories?.data, ["attributes"] as const ).map((category) => category.attributes.short)} metadata={{ currencies: currencies, release_date: libraryItem.attributes.release_date, price: libraryItem.attributes.price, position: "Bottom", }} infoAppend={ !isUntangibleGroupItem( libraryItem.attributes.metadata?.[0] ) && ( ) } />
); })}
)} {selectedTranslation?.text_set?.text && ( <> )}
), [ content.ranged_contents?.data, currencies, languages, langui, selectedTranslation, ] ); const contentPanel = useMemo( () => (
1 ? ( ) : undefined } /> {previousContent?.attributes && (

{langui.previous_content}

({ pre_title: translation.pre_title, title: translation.title, subtitle: translation.subtitle, language: translation.language.data.attributes.code, }))} fallback={{ title: prettySlug(previousContent.attributes.slug), }} languages={languages} thumbnail={ previousContent.attributes.thumbnail?.data?.attributes } thumbnailAspectRatio="3/2" topChips={ isMobile ? undefined : previousContent.attributes.type?.data?.attributes ? [ previousContent.attributes.type.data.attributes .titles?.[0] ? previousContent.attributes.type.data.attributes .titles[0]?.title : prettySlug( previousContent.attributes.type.data.attributes .slug ), ] : undefined } bottomChips={ isMobile ? undefined : previousContent.attributes.categories?.data.map( (category) => category.attributes?.short ?? "" ) } />
)} {nextContent?.attributes && ( <>

{langui.followup_content}

({ pre_title: translation.pre_title, title: translation.title, subtitle: translation.subtitle, language: translation.language.data.attributes.code, }))} fallback={{ title: nextContent.attributes.slug }} languages={languages} thumbnail={nextContent.attributes.thumbnail?.data?.attributes} thumbnailAspectRatio="3/2" topChips={ isMobile ? undefined : nextContent.attributes.type?.data?.attributes ? [ nextContent.attributes.type.data.attributes.titles?.[0] ? nextContent.attributes.type.data.attributes .titles[0]?.title : prettySlug( nextContent.attributes.type.data.attributes.slug ), ] : undefined } bottomChips={ isMobile ? undefined : nextContent.attributes.categories?.data.map( (category) => category.attributes?.short ?? "" ) } /> )}
), [ LanguageSwitcher, content.categories, content.thumbnail?.data?.attributes, content.type, isMobile, languageSwitcherProps, languages, langui, nextContent?.attributes, previousContent?.attributes, selectedTranslation?.description, selectedTranslation?.pre_title, selectedTranslation?.subtitle, selectedTranslation?.text_set?.text, selectedTranslation?.title, ] ); return ( ); }; export default Content; /* * ╭──────────────────────╮ * ───────────────────────────────────╯ NEXT DATA FETCHING ╰────────────────────────────────────── */ export const getStaticProps: GetStaticProps = async (context) => { 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?.data[0]?.attributes?.translations) { return { notFound: true }; } const props: Props = { ...(await getAppStaticProps(context)), content: content.contents.data[0].attributes as ContentWithTranslations, }; return { props: props, }; }; // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ export const getStaticPaths: GetStaticPaths = async (context) => { const sdk = getReadySdk(); const contents = await sdk.getContentsSlugs(); const paths: GetStaticPathsResult["paths"] = []; filterHasAttributes(contents.contents?.data, ["attributes"] as const).map( (item) => { context.locales?.map((local) => { paths.push({ params: { slug: item.attributes.slug }, locale: local, }); }); } ); return { paths, fallback: "blocking", }; }; /* * ╭───────────────────╮ * ─────────────────────────────────────╯ PRIVATE METHODS ╰─────────────────────────────────────── */ type Group = NonNullable< NonNullable< NonNullable< NonNullable["data"] >["attributes"] >["contents"] >["data"]; const getPreviousContent = (group: Group, currentSlug: string) => { for (let index = 0; index < group.length; index++) { const content = group[index]; if (content.attributes?.slug === currentSlug && index > 0) { return group[index - 1]; } } return undefined; }; // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ const getNextContent = (group: Group, currentSlug: string) => { for (let index = 0; index < group.length; index++) { const content = group[index]; if (content.attributes?.slug === currentSlug && index < group.length - 1) { return group[index + 1]; } } return undefined; };