220 lines
6.7 KiB
TypeScript
220 lines
6.7 KiB
TypeScript
import { Fragment, useCallback, useMemo } from "react";
|
|
import { AppLayout, AppLayoutRequired } from "./AppLayout";
|
|
import { Chip } from "./Chip";
|
|
import { HorizontalLine } from "./HorizontalLine";
|
|
import { Markdawn, TableOfContents } from "./Markdown/Markdawn";
|
|
import { ReturnButton } from "./PanelComponents/ReturnButton";
|
|
import { ContentPanel } from "./Panels/ContentPanel";
|
|
import { SubPanel } from "./Panels/SubPanel";
|
|
import { RecorderChip } from "./RecorderChip";
|
|
import { ThumbnailHeader } from "./ThumbnailHeader";
|
|
import { ToolTip } from "./ToolTip";
|
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
|
import { PostWithTranslations } from "helpers/types";
|
|
import { filterHasAttributes, getStatusDescription } from "helpers/others";
|
|
import { prettySlug } from "helpers/formatters";
|
|
import { useAppLayout } from "contexts/AppLayoutContext";
|
|
|
|
/*
|
|
* ╭─────────────╮
|
|
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
|
*/
|
|
|
|
interface Props extends AppLayoutRequired {
|
|
post: PostWithTranslations;
|
|
returnHref?: string;
|
|
returnTitle?: string | null | undefined;
|
|
displayCredits?: boolean;
|
|
displayToc?: boolean;
|
|
displayThumbnailHeader?: boolean;
|
|
displayTitle?: boolean;
|
|
displayLanguageSwitcher?: boolean;
|
|
prependBody?: JSX.Element;
|
|
appendBody?: JSX.Element;
|
|
}
|
|
|
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
|
|
|
export const PostPage = ({
|
|
post,
|
|
returnHref,
|
|
returnTitle,
|
|
displayCredits,
|
|
displayToc,
|
|
displayThumbnailHeader,
|
|
displayLanguageSwitcher,
|
|
appendBody,
|
|
prependBody,
|
|
displayTitle = true,
|
|
...otherProps
|
|
}: Props): JSX.Element => {
|
|
const { langui } = useAppLayout();
|
|
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] =
|
|
useSmartLanguage({
|
|
items: post.translations,
|
|
languageExtractor: useCallback(
|
|
(item: NonNullable<PostWithTranslations["translations"][number]>) =>
|
|
item.language?.data?.attributes?.code,
|
|
[]
|
|
),
|
|
});
|
|
|
|
const { thumbnail, body, title, excerpt } = useMemo(
|
|
() => ({
|
|
thumbnail:
|
|
selectedTranslation?.thumbnail?.data?.attributes ??
|
|
post.thumbnail?.data?.attributes,
|
|
body: selectedTranslation?.body ?? "",
|
|
title: selectedTranslation?.title ?? prettySlug(post.slug),
|
|
excerpt: selectedTranslation?.excerpt ?? "",
|
|
}),
|
|
[post.slug, post.thumbnail, selectedTranslation]
|
|
);
|
|
|
|
const subPanel = useMemo(
|
|
() =>
|
|
returnHref || returnTitle || displayCredits || displayToc ? (
|
|
<SubPanel>
|
|
{returnHref && returnTitle && (
|
|
<ReturnButton
|
|
href={returnHref}
|
|
title={returnTitle}
|
|
displayOnlyOn={"3ColumnsLayout"}
|
|
/>
|
|
)}
|
|
|
|
{displayCredits && (
|
|
<>
|
|
<HorizontalLine />
|
|
|
|
{selectedTranslation && (
|
|
<div className="grid grid-flow-col place-content-center place-items-center gap-2">
|
|
<p className="font-headers font-bold">{langui.status}:</p>
|
|
|
|
<ToolTip
|
|
content={getStatusDescription(
|
|
selectedTranslation.status,
|
|
langui
|
|
)}
|
|
maxWidth={"20rem"}
|
|
>
|
|
<Chip text={selectedTranslation.status} />
|
|
</ToolTip>
|
|
</div>
|
|
)}
|
|
|
|
{post.authors && post.authors.data.length > 0 && (
|
|
<div>
|
|
<p className="font-headers font-bold">{"Authors"}:</p>
|
|
<div className="grid place-content-center place-items-center gap-2">
|
|
{filterHasAttributes(post.authors.data, [
|
|
"id",
|
|
"attributes",
|
|
] as const).map((author) => (
|
|
<Fragment key={author.id}>
|
|
<RecorderChip recorder={author.attributes} />
|
|
</Fragment>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
)}
|
|
|
|
{displayToc && (
|
|
<TableOfContents text={body} title={title} horizontalLine />
|
|
)}
|
|
</SubPanel>
|
|
) : undefined,
|
|
[
|
|
body,
|
|
displayCredits,
|
|
displayToc,
|
|
langui,
|
|
post.authors,
|
|
returnHref,
|
|
returnTitle,
|
|
selectedTranslation,
|
|
title,
|
|
]
|
|
);
|
|
|
|
const contentPanel = useMemo(
|
|
() => (
|
|
<ContentPanel>
|
|
{returnHref && returnTitle && (
|
|
<ReturnButton
|
|
href={returnHref}
|
|
title={returnTitle}
|
|
displayOnlyOn={"1ColumnLayout"}
|
|
className="mb-10"
|
|
/>
|
|
)}
|
|
|
|
{displayThumbnailHeader ? (
|
|
<>
|
|
<ThumbnailHeader
|
|
thumbnail={thumbnail}
|
|
title={title}
|
|
description={excerpt}
|
|
categories={post.categories}
|
|
languageSwitcher={
|
|
languageSwitcherProps.locales.size > 1 ? (
|
|
<LanguageSwitcher {...languageSwitcherProps} />
|
|
) : undefined
|
|
}
|
|
/>
|
|
</>
|
|
) : (
|
|
<>
|
|
{displayLanguageSwitcher && (
|
|
<div className="grid place-content-end place-items-start">
|
|
<LanguageSwitcher {...languageSwitcherProps} />
|
|
</div>
|
|
)}
|
|
{displayTitle && (
|
|
<h1 className="my-16 flex justify-center gap-3 text-center text-4xl">
|
|
{title}
|
|
</h1>
|
|
)}
|
|
</>
|
|
)}
|
|
|
|
{prependBody}
|
|
{body && (
|
|
<>
|
|
{displayThumbnailHeader && <HorizontalLine />}
|
|
<Markdawn text={body} />
|
|
</>
|
|
)}
|
|
|
|
{appendBody}
|
|
</ContentPanel>
|
|
),
|
|
[
|
|
LanguageSwitcher,
|
|
appendBody,
|
|
body,
|
|
displayLanguageSwitcher,
|
|
displayThumbnailHeader,
|
|
displayTitle,
|
|
excerpt,
|
|
languageSwitcherProps,
|
|
post.categories,
|
|
prependBody,
|
|
returnHref,
|
|
returnTitle,
|
|
thumbnail,
|
|
title,
|
|
]
|
|
);
|
|
|
|
return (
|
|
<AppLayout
|
|
{...otherProps}
|
|
contentPanel={contentPanel}
|
|
subPanel={subPanel}
|
|
/>
|
|
);
|
|
};
|