Added previous/next content recommendation

This commit is contained in:
DrMint 2022-04-24 14:28:21 +02:00
parent 2fe1ffb273
commit 1c2653ad07
3 changed files with 367 additions and 112 deletions

View File

@ -0,0 +1,68 @@
import { UploadImageFragment } from "graphql/generated";
import Link from "next/link";
import Chip from "./Chip";
import Img, { ImageQuality } from "./Img";
interface Props {
thumbnail?: UploadImageFragment | string | null | undefined;
thumbnailAspectRatio?: string;
href: string;
pre_title?: string | null | undefined;
title: string | null | undefined;
subtitle?: string | null | undefined;
topChips?: string[];
bottomChips?: string[];
}
export default function PreviewLine(props: Props): JSX.Element {
const {
href,
thumbnail,
pre_title,
title,
subtitle,
topChips,
bottomChips,
thumbnailAspectRatio,
} = props;
return (
<Link href={href} passHref>
<div
className="drop-shadow-shade-xl rounded-md bg-light cursor-pointer hover:scale-[1.02]
transition-transform flex flex-row gap-4 overflow-hidden place-items-center pr-4 w-full h-36"
>
{thumbnail ? (
<div className="h-full aspect-[3/2]">
<Img image={thumbnail} quality={ImageQuality.Medium} />
</div>
) : (
<div style={{ aspectRatio: thumbnailAspectRatio }}></div>
)}
<div className="grid gap-2">
{topChips && topChips.length > 0 && (
<div className="grid grid-flow-col gap-1 overflow-hidden place-content-start">
{topChips.map((text, index) => (
<Chip key={index}>{text}</Chip>
))}
</div>
)}
<div className="flex flex-col">
{pre_title && <p>{pre_title}</p>}
{title && <h1 className="text-lg">{title}</h1>}
{subtitle && <h2>{subtitle}</h2>}
</div>
{bottomChips && bottomChips.length > 0 && (
<div className="grid grid-flow-col gap-1 overflow-hidden place-content-start">
{bottomChips.map((text, index) => (
<Chip key={index} className="text-sm">
{text}
</Chip>
))}
</div>
)}
</div>
</div>
</Link>
);
}

View File

@ -106,6 +106,84 @@ query getContentText($slug: String, $language_code: String) {
} }
} }
} }
previous_recommended {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
pre_title
title
subtitle
}
categories {
data {
id
attributes {
short
}
}
}
type {
data {
attributes {
slug
titles(
filters: { language: { code: { eq: $language_code } } }
) {
title
}
}
}
}
thumbnail {
data {
attributes {
...uploadImage
}
}
}
}
}
}
next_recommended {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
pre_title
title
subtitle
}
categories {
data {
id
attributes {
short
}
}
}
type {
data {
attributes {
slug
titles(
filters: { language: { code: { eq: $language_code } } }
) {
title
}
}
}
}
thumbnail {
data {
attributes {
...uploadImage
}
}
}
}
}
}
} }
} }
} }

View File

@ -9,12 +9,14 @@ import ReturnButton, {
} from "components/PanelComponents/ReturnButton"; } from "components/PanelComponents/ReturnButton";
import ContentPanel from "components/Panels/ContentPanel"; import ContentPanel from "components/Panels/ContentPanel";
import SubPanel from "components/Panels/SubPanel"; import SubPanel from "components/Panels/SubPanel";
import PreviewLine from "components/PreviewLine";
import RecorderChip from "components/RecorderChip"; import RecorderChip from "components/RecorderChip";
import ThumbnailHeader from "components/ThumbnailHeader"; import ThumbnailHeader from "components/ThumbnailHeader";
import ToolTip from "components/ToolTip"; import ToolTip from "components/ToolTip";
import { useAppLayout } from "contexts/AppLayoutContext"; import { useAppLayout } from "contexts/AppLayoutContext";
import { GetContentTextQuery } from "graphql/generated"; import { GetContentTextQuery } from "graphql/generated";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";
import { useMediaMobile } from "hooks/useMediaQuery";
import { import {
GetStaticPathsContext, GetStaticPathsContext,
GetStaticPathsResult, GetStaticPathsResult,
@ -47,6 +49,8 @@ export default function Content(props: Props): JSX.Element {
const router = useRouter(); const router = useRouter();
const appLayout = useAppLayout(); const appLayout = useAppLayout();
const isMobile = useMediaMobile();
const [selectedTextSet, setSelectedTextSet] = useState< const [selectedTextSet, setSelectedTextSet] = useState<
| Exclude< | Exclude<
Exclude<Props["content"], null | undefined>["text_set"], Exclude<Props["content"], null | undefined>["text_set"],
@ -261,9 +265,111 @@ export default function Content(props: Props): JSX.Element {
} }
/> />
{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 /> <HorizontalLine />
<Markdawn text={selectedTextSet?.text ?? ""} /> <Markdawn text={selectedTextSet?.text ?? ""} />
<HorizontalLine />
{content.next_recommended?.data?.attributes && (
<>
<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> </div>
)} )}
</ContentPanel> </ContentPanel>
@ -338,7 +444,10 @@ export async function getStaticPaths(
contents.contents?.data.map((item) => { contents.contents?.data.map((item) => {
context.locales?.map((local) => { context.locales?.map((local) => {
if (item.attributes) if (item.attributes)
paths.push({ params: { slug: item.attributes.slug }, locale: local }); paths.push({
params: { slug: item.attributes.slug },
locale: local,
});
}); });
}); });
return { return {