Added previous/next content recommendation
This commit is contained in:
parent
2fe1ffb273
commit
1c2653ad07
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,112 +1,190 @@
|
||||||
query getContentText($slug: String, $language_code: String) {
|
query getContentText($slug: String, $language_code: String) {
|
||||||
contents(filters: { slug: { eq: $slug } }) {
|
contents(filters: { slug: { eq: $slug } }) {
|
||||||
data {
|
data {
|
||||||
id
|
id
|
||||||
attributes {
|
attributes {
|
||||||
slug
|
slug
|
||||||
titles {
|
titles {
|
||||||
pre_title
|
pre_title
|
||||||
title
|
title
|
||||||
subtitle
|
subtitle
|
||||||
description
|
description
|
||||||
}
|
}
|
||||||
categories {
|
categories {
|
||||||
data {
|
data {
|
||||||
id
|
id
|
||||||
attributes {
|
attributes {
|
||||||
name
|
name
|
||||||
short
|
short
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type {
|
type {
|
||||||
data {
|
data {
|
||||||
attributes {
|
attributes {
|
||||||
slug
|
slug
|
||||||
titles(filters: { language: { code: { eq: $language_code } } }) {
|
titles(filters: { language: { code: { eq: $language_code } } }) {
|
||||||
title
|
title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ranged_contents {
|
ranged_contents {
|
||||||
data {
|
data {
|
||||||
id
|
id
|
||||||
attributes {
|
attributes {
|
||||||
slug
|
slug
|
||||||
scan_set {
|
scan_set {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
library_item {
|
library_item {
|
||||||
data {
|
data {
|
||||||
attributes {
|
attributes {
|
||||||
slug
|
slug
|
||||||
title
|
title
|
||||||
subtitle
|
subtitle
|
||||||
thumbnail {
|
thumbnail {
|
||||||
data {
|
data {
|
||||||
attributes {
|
attributes {
|
||||||
...uploadImage
|
...uploadImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
text_set {
|
text_set {
|
||||||
status
|
status
|
||||||
text
|
text
|
||||||
language {
|
language {
|
||||||
data {
|
data {
|
||||||
attributes {
|
attributes {
|
||||||
code
|
code
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
source_language {
|
source_language {
|
||||||
data {
|
data {
|
||||||
attributes {
|
attributes {
|
||||||
code
|
code
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
transcribers {
|
transcribers {
|
||||||
data {
|
data {
|
||||||
id
|
id
|
||||||
attributes {
|
attributes {
|
||||||
...recorderChip
|
...recorderChip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
translators {
|
translators {
|
||||||
data {
|
data {
|
||||||
id
|
id
|
||||||
attributes {
|
attributes {
|
||||||
...recorderChip
|
...recorderChip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
proofreaders {
|
proofreaders {
|
||||||
data {
|
data {
|
||||||
id
|
id
|
||||||
attributes {
|
attributes {
|
||||||
...recorderChip
|
...recorderChip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
notes
|
notes
|
||||||
}
|
}
|
||||||
thumbnail {
|
thumbnail {
|
||||||
data {
|
data {
|
||||||
attributes {
|
attributes {
|
||||||
...uploadImage
|
...uploadImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue