Use new content groups instead of previous/next

This commit is contained in:
DrMint 2022-05-15 01:15:29 +02:00
parent 461bd08a3d
commit 2e62f25fb5
6 changed files with 346 additions and 338 deletions

View File

@ -28,7 +28,7 @@ interface Props {
topChips?: string[]; topChips?: string[];
bottomChips?: string[]; bottomChips?: string[];
keepInfoVisible?: boolean; keepInfoVisible?: boolean;
stackEffect?: boolean; stackNumber?: number;
metadata?: { metadata?: {
currencies?: AppStaticProps["currencies"]; currencies?: AppStaticProps["currencies"];
release_date?: DatePickerFragment | null; release_date?: DatePickerFragment | null;
@ -53,7 +53,7 @@ export function PreviewCard(props: Immutable<Props>): JSX.Element {
title, title,
subtitle, subtitle,
description, description,
stackEffect = false, stackNumber = 0,
topChips, topChips,
bottomChips, bottomChips,
keepInfoVisible, keepInfoVisible,
@ -117,20 +117,27 @@ export function PreviewCard(props: Immutable<Props>): JSX.Element {
hover:[--play-opacity:100] transition-transform hover:[--play-opacity:100] transition-transform
[--stacked-top:0] hover:[--stacked-top:1]" [--stacked-top:0] hover:[--stacked-top:1]"
> >
{thumbnail && stackEffect && ( {thumbnail && stackNumber > 0 && (
<> <>
<Img <Img
className={`rounded-t-md absolute -top-[var(--stacked-top)*1.3rem] className="opacity-30 rounded-t-md overflow-hidden absolute transition-[top_transform]
opacity-30 scale-[0.88] transition-[top]`} -top-[var(--stacked-top)*1.5rem]
image={thumbnail} scale-[calc(1-0.15*var(--stacked-top))]"
quality={ImageQuality.Medium}
/>
<Img
className={`rounded-t-md absolute -top-[var(--stacked-top)*0.6rem]
opacity-60 scale-95 transition-[top]`}
image={thumbnail} image={thumbnail}
quality={ImageQuality.Medium} quality={ImageQuality.Medium}
/> />
<div
className="bg-light rounded-t-md overflow-hidden absolute transition-[top_transform]
-top-[var(--stacked-top)*0.7rem]
scale-[calc(1-0.06*var(--stacked-top))]"
>
<Img
className="opacity-70"
image={thumbnail}
quality={ImageQuality.Medium}
/>
</div>
</> </>
)} )}
@ -145,6 +152,14 @@ export function PreviewCard(props: Immutable<Props>): JSX.Element {
image={thumbnail} image={thumbnail}
quality={ImageQuality.Medium} quality={ImageQuality.Medium}
/> />
{stackNumber > 0 && (
<div
className="absolute right-2 top-2 text-light bg-black
bg-opacity-60 px-2 rounded-full"
>
{stackNumber}
</div>
)}
{hoverlay && hoverlay.__typename === "Video" && ( {hoverlay && hoverlay.__typename === "Video" && (
<> <>
<div <div

View File

@ -1,75 +1,64 @@
query devGetContents { query devGetContents {
contents(pagination: { limit: -1 }) { contents(pagination: { limit: -1 }) {
data { data {
id id
attributes { attributes {
slug slug
categories { categories {
data { data {
id id
} }
} }
type { type {
data { data {
id id
} }
} }
ranged_contents { ranged_contents {
data { data {
id id
} }
} }
translations { translations {
language { language {
data { data {
id id
} }
} }
title title
description description
text_set { text_set {
source_language { source_language {
data { data {
id id
} }
} }
status status
transcribers { transcribers {
data { data {
id id
} }
} }
translators { translators {
data { data {
id id
} }
} }
proofreaders { proofreaders {
data { data {
id id
} }
} }
text text
} }
} }
thumbnail {
thumbnail { data {
data { id
id }
} }
} }
next_recommended { }
data { }
id
}
}
previous_recommended {
data {
id
}
}
}
}
}
} }

View File

@ -1,192 +1,161 @@
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
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
} }
} }
} }
} }
} }
} }
} }
} }
} }
translations { translations {
language { language {
data { data {
attributes { attributes {
code code
} }
} }
} }
pre_title pre_title
title title
subtitle subtitle
description description
text_set { text_set {
status status
text text
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 { group {
data { data {
attributes { attributes {
slug contents {
translations { data {
pre_title attributes {
title slug
subtitle translations {
} pre_title
categories { title
data { subtitle
id }
attributes { categories {
short data {
} id
} attributes {
} short
type { }
data { }
attributes { }
slug type {
titles( data {
filters: { language: { code: { eq: $language_code } } } attributes {
) { slug
title titles(
} filters: {
} language: { code: { eq: $language_code } }
} }
} ) {
thumbnail { title
data { }
attributes { }
...uploadImage }
} }
} thumbnail {
} data {
} attributes {
} ...uploadImage
} }
next_recommended { }
data { }
attributes { }
slug }
translations { }
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

@ -5,13 +5,6 @@ query getContents($language_code: String) {
attributes { attributes {
slug slug
translations { translations {
language {
data {
attributes {
code
}
}
}
pre_title pre_title
title title
subtitle subtitle
@ -62,14 +55,16 @@ query getContents($language_code: String) {
} }
} }
} }
next_recommended { group {
data { data {
id attributes {
} combine
} contents {
previous_recommended { data {
data { id
id }
}
}
} }
} }
thumbnail { thumbnail {

View File

@ -34,6 +34,48 @@ interface Props extends AppStaticProps {
content: ContentWithTranslations; content: ContentWithTranslations;
} }
function getPreviousContent(
group: Immutable<
NonNullable<
NonNullable<
NonNullable<
NonNullable<ContentWithTranslations["group"]>["data"]
>["attributes"]
>["contents"]
>["data"]
>,
currentSlug: string
) {
for (let index = 0; index < group.length; index += 1) {
const content = group[index];
if (content.attributes?.slug === currentSlug && index > 0) {
return group[index - 1];
}
}
return undefined;
}
function getNextContent(
group: Immutable<
NonNullable<
NonNullable<
NonNullable<
NonNullable<ContentWithTranslations["group"]>["data"]
>["attributes"]
>["contents"]
>["data"]
>,
currentSlug: string
) {
for (let index = 0; index < group.length; index += 1) {
const content = group[index];
if (content.attributes?.slug === currentSlug && index < group.length - 1) {
return group[index + 1];
}
}
return undefined;
}
export default function Content(props: Immutable<Props>): JSX.Element { export default function Content(props: Immutable<Props>): JSX.Element {
const { langui, content, languages } = props; const { langui, content, languages } = props;
const isMobile = useMediaMobile(); const isMobile = useMediaMobile();
@ -44,6 +86,17 @@ export default function Content(props: Immutable<Props>): JSX.Element {
languageExtractor: (item) => item.language?.data?.attributes?.code, languageExtractor: (item) => item.language?.data?.attributes?.code,
}); });
const previousContent = content.group?.data?.attributes?.contents
? getPreviousContent(
content.group.data.attributes.contents.data,
content.slug
)
: undefined;
const nextContent = content.group?.data?.attributes?.contents
? getNextContent(content.group.data.attributes.contents.data, content.slug)
: undefined;
const subPanel = ( const subPanel = (
<SubPanel> <SubPanel>
<ReturnButton <ReturnButton
@ -210,42 +263,37 @@ export default function Content(props: Immutable<Props>): JSX.Element {
languageSwitcher={<LanguageSwitcher />} languageSwitcher={<LanguageSwitcher />}
/> />
{content.previous_recommended?.data?.attributes && ( {previousContent?.attributes && (
<div className="mt-12 mb-8 w-full"> <div className="mt-12 mb-8 w-full">
<h2 className="text-center text-2xl mb-4">Previous content</h2> <h2 className="text-center text-2xl mb-4">Previous content</h2>
<PreviewLine <PreviewLine
href={`/contents/${content.previous_recommended.data.attributes.slug}`} href={`/contents/${previousContent.attributes.slug}`}
pre_title={ pre_title={
content.previous_recommended.data.attributes.translations?.[0] previousContent.attributes.translations?.[0]?.pre_title
?.pre_title
} }
title={ title={
content.previous_recommended.data.attributes.translations?.[0] previousContent.attributes.translations?.[0]?.title ??
?.title ?? prettySlug(previousContent.attributes.slug)
prettySlug(content.previous_recommended.data.attributes.slug)
} }
subtitle={ subtitle={
content.previous_recommended.data.attributes.translations?.[0] previousContent.attributes.translations?.[0]?.subtitle
?.subtitle
} }
thumbnail={ thumbnail={
content.previous_recommended.data.attributes.thumbnail?.data previousContent.attributes.thumbnail?.data?.attributes
?.attributes
} }
thumbnailAspectRatio="3/2" thumbnailAspectRatio="3/2"
topChips={ topChips={
isMobile isMobile
? undefined ? undefined
: content.previous_recommended.data.attributes.type?.data : previousContent.attributes.type?.data?.attributes
?.attributes
? [ ? [
content.previous_recommended.data.attributes.type.data previousContent.attributes.type.data.attributes
.attributes.titles?.[0] .titles?.[0]
? content.previous_recommended.data.attributes.type ? previousContent.attributes.type.data.attributes
.data.attributes.titles[0]?.title .titles[0]?.title
: prettySlug( : prettySlug(
content.previous_recommended.data.attributes.type previousContent.attributes.type.data.attributes
.data.attributes.slug .slug
), ),
] ]
: undefined : undefined
@ -253,7 +301,7 @@ export default function Content(props: Immutable<Props>): JSX.Element {
bottomChips={ bottomChips={
isMobile isMobile
? undefined ? undefined
: content.previous_recommended.data.attributes.categories?.data.map( : previousContent.attributes.categories?.data.map(
(category) => category.attributes?.short ?? "" (category) => category.attributes?.short ?? ""
) )
} }
@ -265,43 +313,30 @@ export default function Content(props: Immutable<Props>): JSX.Element {
<Markdawn text={selectedTranslation?.text_set?.text ?? ""} /> <Markdawn text={selectedTranslation?.text_set?.text ?? ""} />
{content.next_recommended?.data?.attributes && ( {nextContent?.attributes && (
<> <>
<HorizontalLine /> <HorizontalLine />
<h2 className="text-center text-2xl mb-4">Follow-up content</h2> <h2 className="text-center text-2xl mb-4">Follow-up content</h2>
<PreviewLine <PreviewLine
href={`/contents/${content.next_recommended.data.attributes.slug}`} href={`/contents/${nextContent.attributes.slug}`}
pre_title={ pre_title={nextContent.attributes.translations?.[0]?.pre_title}
content.next_recommended.data.attributes.translations?.[0]
?.pre_title
}
title={ title={
content.next_recommended.data.attributes.translations?.[0] nextContent.attributes.translations?.[0]?.title ??
?.title ?? prettySlug(nextContent.attributes.slug)
prettySlug(content.next_recommended.data.attributes.slug)
}
subtitle={
content.next_recommended.data.attributes.translations?.[0]
?.subtitle
}
thumbnail={
content.next_recommended.data.attributes.thumbnail?.data
?.attributes
} }
subtitle={nextContent.attributes.translations?.[0]?.subtitle}
thumbnail={nextContent.attributes.thumbnail?.data?.attributes}
thumbnailAspectRatio="3/2" thumbnailAspectRatio="3/2"
topChips={ topChips={
isMobile isMobile
? undefined ? undefined
: content.next_recommended.data.attributes.type?.data : nextContent.attributes.type?.data?.attributes
?.attributes
? [ ? [
content.next_recommended.data.attributes.type.data nextContent.attributes.type.data.attributes.titles?.[0]
.attributes.titles?.[0] ? nextContent.attributes.type.data.attributes
? content.next_recommended.data.attributes.type.data .titles[0]?.title
.attributes.titles[0]?.title
: prettySlug( : prettySlug(
content.next_recommended.data.attributes.type.data nextContent.attributes.type.data.attributes.slug
.attributes.slug
), ),
] ]
: undefined : undefined
@ -309,7 +344,7 @@ export default function Content(props: Immutable<Props>): JSX.Element {
bottomChips={ bottomChips={
isMobile isMobile
? undefined ? undefined
: content.next_recommended.data.attributes.categories?.data.map( : nextContent.attributes.categories?.data.map(
(category) => category.attributes?.short ?? "" (category) => category.attributes?.short ?? ""
) )
} }

View File

@ -18,7 +18,7 @@ import { GetStaticPropsContext } from "next";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
interface Props extends AppStaticProps { interface Props extends AppStaticProps {
contents: Exclude<GetContentsQuery["contents"], null | undefined>["data"]; contents: NonNullable<GetContentsQuery["contents"]>["data"];
} }
type GroupContentItems = Map<string, Immutable<Props["contents"]>>; type GroupContentItems = Map<string, Immutable<Props["contents"]>>;
@ -119,11 +119,12 @@ export default function Contents(props: Immutable<Props>): JSX.Element {
subtitle={item.attributes.translations?.[0]?.subtitle} subtitle={item.attributes.translations?.[0]?.subtitle}
thumbnail={item.attributes.thumbnail?.data?.attributes} thumbnail={item.attributes.thumbnail?.data?.attributes}
thumbnailAspectRatio="3/2" thumbnailAspectRatio="3/2"
stackEffect={ stackNumber={
item.attributes.next_recommended?.data?.id !== null && combineRelatedContent &&
item.attributes.next_recommended?.data?.id !== item.attributes.group?.data?.attributes?.combine
undefined && ? item.attributes.group?.data?.attributes.contents
combineRelatedContent ?.data.length
: 0
} }
topChips={ topChips={
item.attributes.type?.data?.attributes item.attributes.type?.data?.attributes
@ -263,7 +264,11 @@ function filterContents(
): Immutable<Props["contents"]> { ): Immutable<Props["contents"]> {
if (combineRelatedContent) { if (combineRelatedContent) {
return [...contents].filter( return [...contents].filter(
(content) => !content.attributes?.previous_recommended?.data?.id (content) =>
!content.attributes?.group?.data?.attributes ||
!content.attributes.group.data.attributes.combine ||
content.attributes.group.data.attributes.contents?.data?.[0].id ===
content.id
); );
} }
return contents; return contents;