Simplified some DTO
This commit is contained in:
parent
7efa43a630
commit
da916f898a
|
@ -22,7 +22,6 @@ import { useTypedRouter } from "hooks/useTypedRouter";
|
|||
import { Select } from "components/Inputs/Select";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { Button } from "components/Inputs/Button";
|
||||
import { GetVideoChannelQuery } from "graphql/generated";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { Paginator } from "components/Containers/Paginator";
|
||||
import { useFormat } from "hooks/useFormat";
|
||||
|
@ -56,9 +55,11 @@ const queryParamSchema = z.object({
|
|||
*/
|
||||
|
||||
interface Props extends AppLayoutRequired {
|
||||
channel: NonNullable<
|
||||
NonNullable<GetVideoChannelQuery["videoChannels"]>["data"][number]["attributes"]
|
||||
>;
|
||||
channel: {
|
||||
uid: string;
|
||||
title: string;
|
||||
subscribers: number;
|
||||
};
|
||||
}
|
||||
|
||||
const Channel = ({ channel, ...otherProps }: Props): JSX.Element => {
|
||||
|
@ -282,14 +283,23 @@ export default Channel;
|
|||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const { format } = getFormat(context.locale);
|
||||
const channel = await sdk.getVideoChannel({
|
||||
const videoChannel = (
|
||||
await sdk.getVideoChannel({
|
||||
channel: context.params && isDefined(context.params.uid) ? context.params.uid.toString() : "",
|
||||
});
|
||||
if (!channel.videoChannels?.data[0]?.attributes) return { notFound: true };
|
||||
})
|
||||
).videoChannels?.data[0]?.attributes;
|
||||
|
||||
if (!videoChannel) return { notFound: true };
|
||||
|
||||
const channel: Props["channel"] = {
|
||||
uid: videoChannel.uid,
|
||||
subscribers: videoChannel.subscribers,
|
||||
title: videoChannel.title,
|
||||
};
|
||||
|
||||
const props: Props = {
|
||||
channel: channel.videoChannels.data[0].attributes,
|
||||
openGraph: getOpenGraph(format, channel.videoChannels.data[0].attributes.title),
|
||||
channel,
|
||||
openGraph: getOpenGraph(format, channel.title),
|
||||
};
|
||||
return {
|
||||
props: props,
|
||||
|
|
|
@ -9,10 +9,10 @@ import { NavOption } from "components/PanelComponents/NavOption";
|
|||
import { ReturnButton } from "components/PanelComponents/ReturnButton";
|
||||
import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
|
||||
import { SubPanel } from "components/Containers/SubPanel";
|
||||
import { GetVideoQuery } from "graphql/generated";
|
||||
import { Enum_Video_Source } from "graphql/generated";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { prettyShortenNumber } from "helpers/formatters";
|
||||
import { filterHasAttributes, isDefined } from "helpers/asserts";
|
||||
import { filterHasAttributes, isDefined, isUndefined } from "helpers/asserts";
|
||||
import { getVideoFile, getVideoThumbnailURL } from "helpers/videos";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { atoms } from "contexts/atoms";
|
||||
|
@ -29,15 +29,29 @@ import { Markdown } from "components/Markdown/Markdown";
|
|||
*/
|
||||
|
||||
interface Props extends AppLayoutRequired {
|
||||
video: NonNullable<NonNullable<GetVideoQuery["videos"]>["data"][number]["attributes"]>;
|
||||
video: {
|
||||
isGone: boolean;
|
||||
uid: string;
|
||||
title: string;
|
||||
description: string;
|
||||
publishedDate: string;
|
||||
views: number;
|
||||
likes: number;
|
||||
source?: Enum_Video_Source;
|
||||
};
|
||||
channel?: {
|
||||
title: string;
|
||||
href: string;
|
||||
subscribers: number;
|
||||
};
|
||||
}
|
||||
|
||||
const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
||||
const Video = ({ video, channel, ...otherProps }: Props): JSX.Element => {
|
||||
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||
const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened);
|
||||
const closeSubPanel = useCallback(() => setSubPanelOpened(false), [setSubPanelOpened]);
|
||||
const { format, formatDate } = useFormat();
|
||||
const { format } = useFormat();
|
||||
|
||||
const subPanel = (
|
||||
<SubPanel>
|
||||
|
@ -62,7 +76,7 @@ const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
|||
|
||||
<div className="grid place-items-center gap-12">
|
||||
<div id="video" className="w-full overflow-hidden rounded-xl shadow-xl shadow-shade/80">
|
||||
{video.gone ? (
|
||||
{video.isGone ? (
|
||||
<VideoPlayer className="w-full" src={getVideoFile(video.uid)} rounded={false} />
|
||||
) : (
|
||||
<iframe
|
||||
|
@ -80,7 +94,7 @@ const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
|||
<div className="flex w-full flex-row flex-wrap place-items-center gap-x-6">
|
||||
<p>
|
||||
<Ico icon="event" className="mr-1 translate-y-[.15em] !text-base" />
|
||||
{formatDate(video.published_date)}
|
||||
{video.publishedDate}
|
||||
</p>
|
||||
<p>
|
||||
<Ico icon="visibility" className="mr-1 translate-y-[.15em] !text-base" />
|
||||
|
@ -88,7 +102,7 @@ const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
|||
? video.views.toLocaleString()
|
||||
: prettyShortenNumber(video.views)}
|
||||
</p>
|
||||
{video.channel?.data?.attributes && (
|
||||
{video.likes > 0 && (
|
||||
<p>
|
||||
<Ico icon="thumb_up" className="mr-1 translate-y-[.15em] !text-base" />
|
||||
{isContentPanelAtLeast4xl
|
||||
|
@ -108,18 +122,14 @@ const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{video.channel?.data?.attributes && (
|
||||
{channel && (
|
||||
<InsetBox id="channel" className="grid place-items-center">
|
||||
<div className="grid w-[clamp(0px,100%,42rem)] place-items-center gap-4 text-center">
|
||||
<h2 className="text-2xl">{format("channel")}</h2>
|
||||
<div>
|
||||
<Button
|
||||
href={`/archives/videos/c/${video.channel.data.attributes.uid}\
|
||||
?page=1&query=&sort=1&gone=`}
|
||||
text={video.channel.data.attributes.title}
|
||||
/>
|
||||
<Button href={channel.href} text={channel.title} />
|
||||
<p>
|
||||
{`${video.channel.data.attributes.subscribers.toLocaleString()}
|
||||
{`${channel.subscribers.toLocaleString()}
|
||||
${format("subscribers").toLowerCase()}`}
|
||||
</p>
|
||||
</div>
|
||||
|
@ -148,29 +158,48 @@ export default Video;
|
|||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const { format } = getFormat(context.locale);
|
||||
const { format, formatDate } = getFormat(context.locale);
|
||||
const videos = await sdk.getVideo({
|
||||
uid: context.params && isDefined(context.params.uid) ? context.params.uid.toString() : "",
|
||||
});
|
||||
if (!videos.videos?.data[0]?.attributes) return { notFound: true };
|
||||
const rawVideo = videos.videos?.data[0]?.attributes;
|
||||
if (isUndefined(rawVideo)) return { notFound: true };
|
||||
|
||||
const channel: Props["channel"] = rawVideo.channel?.data?.attributes
|
||||
? {
|
||||
href: `/archives/videos/c/${rawVideo.channel.data.attributes.uid}`,
|
||||
subscribers: rawVideo.channel.data.attributes.subscribers,
|
||||
title: rawVideo.channel.data.attributes.title,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const video: Props["video"] = {
|
||||
uid: rawVideo.uid,
|
||||
isGone: rawVideo.gone,
|
||||
description: rawVideo.description,
|
||||
likes: rawVideo.likes,
|
||||
source: rawVideo.source ?? undefined,
|
||||
publishedDate: formatDate(rawVideo.published_date),
|
||||
title: rawVideo.title,
|
||||
views: rawVideo.views,
|
||||
};
|
||||
|
||||
const props: Props = {
|
||||
video: videos.videos.data[0].attributes,
|
||||
video,
|
||||
channel,
|
||||
openGraph: getOpenGraph(
|
||||
format,
|
||||
videos.videos.data[0].attributes.title,
|
||||
getDescription(videos.videos.data[0].attributes.description, {
|
||||
[format("channel")]: [videos.videos.data[0].attributes.channel?.data?.attributes?.title],
|
||||
rawVideo.title,
|
||||
getDescription(rawVideo.description, {
|
||||
[format("channel")]: [rawVideo.channel?.data?.attributes?.title],
|
||||
}),
|
||||
getVideoThumbnailURL(videos.videos.data[0].attributes.uid),
|
||||
getVideoThumbnailURL(rawVideo.uid),
|
||||
undefined,
|
||||
videos.videos.data[0].attributes.gone
|
||||
? getVideoFile(videos.videos.data[0].attributes.uid)
|
||||
: undefined
|
||||
rawVideo.gone ? getVideoFile(rawVideo.uid) : undefined
|
||||
),
|
||||
};
|
||||
return {
|
||||
props: props,
|
||||
props: JSON.parse(JSON.stringify(props)),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/Cont
|
|||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { filterHasAttributes } from "helpers/asserts";
|
||||
import { GetContentsFolderQuery, ParentFolderPreviewFragment } from "graphql/generated";
|
||||
import { ParentFolderPreviewFragment, UploadImageFragment } from "graphql/generated";
|
||||
import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/locales";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
import { Button } from "components/Inputs/Button";
|
||||
|
@ -27,14 +27,32 @@ import { FolderPath } from "components/Contents/FolderPath";
|
|||
*/
|
||||
|
||||
interface Props extends AppLayoutRequired {
|
||||
folder: NonNullable<
|
||||
NonNullable<GetContentsFolderQuery["contentsFolders"]>["data"][number]["attributes"]
|
||||
>;
|
||||
subfolders: {
|
||||
slug: string;
|
||||
href: string;
|
||||
translations: { language: string; title: string }[];
|
||||
fallback: { title: string };
|
||||
}[];
|
||||
contents: {
|
||||
slug: string;
|
||||
href: string;
|
||||
translations: { language: string; pre_title?: string; title: string; subtitle?: string }[];
|
||||
fallback: { title: string };
|
||||
thumbnail?: UploadImageFragment;
|
||||
topChips?: string[];
|
||||
bottomChips?: string[];
|
||||
}[];
|
||||
path: ParentFolderPreviewFragment[];
|
||||
}
|
||||
|
||||
const ContentsFolder = ({ openGraph, folder, path, ...otherProps }: Props): JSX.Element => {
|
||||
const { format, formatCategory, formatContentType } = useFormat();
|
||||
const ContentsFolder = ({
|
||||
openGraph,
|
||||
path,
|
||||
contents,
|
||||
subfolders,
|
||||
...otherProps
|
||||
}: Props): JSX.Element => {
|
||||
const { format } = useFormat();
|
||||
const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened);
|
||||
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||
|
||||
|
@ -61,11 +79,11 @@ const ContentsFolder = ({ openGraph, folder, path, ...otherProps }: Props): JSX.
|
|||
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
||||
<FolderPath path={path} />
|
||||
|
||||
{folder.subfolders?.data && folder.subfolders.data.length > 0 && (
|
||||
{subfolders.length > 0 && (
|
||||
<div className="mb-8">
|
||||
<div className="mb-2 flex place-items-center gap-2">
|
||||
<h2 className="text-2xl">{format("folders")}</h2>
|
||||
<Chip text={format("x_results", { x: folder.subfolders.data.length })} />
|
||||
<Chip text={format("x_results", { x: subfolders.length })} />
|
||||
</div>
|
||||
<div
|
||||
className={cJoin(
|
||||
|
@ -76,28 +94,18 @@ const ContentsFolder = ({ openGraph, folder, path, ...otherProps }: Props): JSX.
|
|||
"grid-cols-2 gap-4"
|
||||
)
|
||||
)}>
|
||||
{filterHasAttributes(folder.subfolders.data, ["id", "attributes"]).map((subfolder) => (
|
||||
<TranslatedPreviewFolder
|
||||
key={subfolder.id}
|
||||
href={`/contents/folder/${subfolder.attributes.slug}`}
|
||||
translations={filterHasAttributes(subfolder.attributes.titles, [
|
||||
"language.data.attributes.code",
|
||||
]).map((title) => ({
|
||||
title: title.title,
|
||||
language: title.language.data.attributes.code,
|
||||
}))}
|
||||
fallback={{ title: prettySlug(subfolder.attributes.slug) }}
|
||||
/>
|
||||
{subfolders.map((subfolder) => (
|
||||
<TranslatedPreviewFolder key={subfolder.slug} {...subfolder} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{folder.contents?.data && folder.contents.data.length > 0 && (
|
||||
{contents.length > 0 && (
|
||||
<div className="mb-8">
|
||||
<div className="mb-2 flex place-items-center gap-2">
|
||||
<h2 className="text-2xl">{format("contents")}</h2>
|
||||
<Chip text={format("x_results", { x: folder.contents.data.length })} />
|
||||
<Chip text={format("x_results", { x: contents.length })} />
|
||||
</div>
|
||||
<div
|
||||
className={cJoin(
|
||||
|
@ -108,30 +116,12 @@ const ContentsFolder = ({ openGraph, folder, path, ...otherProps }: Props): JSX.
|
|||
"grid-cols-2 gap-4"
|
||||
)
|
||||
)}>
|
||||
{filterHasAttributes(folder.contents.data, ["id", "attributes"]).map((item) => (
|
||||
{contents.map((item) => (
|
||||
<TranslatedPreviewCard
|
||||
key={item.id}
|
||||
href={`/contents/${item.attributes.slug}`}
|
||||
translations={filterHasAttributes(item.attributes.translations, [
|
||||
"language.data.attributes.code",
|
||||
]).map((translation) => ({
|
||||
pre_title: translation.pre_title,
|
||||
title: translation.title,
|
||||
subtitle: translation.subtitle,
|
||||
language: translation.language.data.attributes.code,
|
||||
}))}
|
||||
fallback={{ title: prettySlug(item.attributes.slug) }}
|
||||
thumbnail={item.attributes.thumbnail?.data?.attributes}
|
||||
key={item.slug}
|
||||
{...item}
|
||||
thumbnailAspectRatio="3/2"
|
||||
thumbnailForceAspectRatio
|
||||
topChips={
|
||||
item.attributes.type?.data?.attributes
|
||||
? [formatContentType(item.attributes.type.data.attributes.slug)]
|
||||
: undefined
|
||||
}
|
||||
bottomChips={filterHasAttributes(item.attributes.categories?.data, [
|
||||
"attributes",
|
||||
]).map((category) => formatCategory(category.attributes.slug))}
|
||||
keepInfoVisible
|
||||
/>
|
||||
))}
|
||||
|
@ -139,9 +129,7 @@ const ContentsFolder = ({ openGraph, folder, path, ...otherProps }: Props): JSX.
|
|||
</div>
|
||||
)}
|
||||
|
||||
{folder.contents?.data.length === 0 && folder.subfolders?.data.length === 0 && (
|
||||
<NoContentNorFolderMessage />
|
||||
)}
|
||||
{contents.length === 0 && subfolders.length === 0 && <NoContentNorFolderMessage />}
|
||||
</ContentPanel>
|
||||
);
|
||||
|
||||
|
@ -163,7 +151,7 @@ export default ContentsFolder;
|
|||
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const sdk = getReadySdk();
|
||||
const { format } = getFormat(context.locale);
|
||||
const { format, formatContentType, formatCategory } = getFormat(context.locale);
|
||||
const slug = context.params?.slug ? context.params.slug.toString() : "";
|
||||
const contentsFolder = await sdk.getContentsFolder({ slug: slug });
|
||||
if (!contentsFolder.contentsFolders?.data[0]?.attributes) {
|
||||
|
@ -189,13 +177,51 @@ export const getStaticProps: GetStaticProps = async (context) => {
|
|||
return prettySlug(folder.slug);
|
||||
})();
|
||||
|
||||
const subfolders: Props["subfolders"] = filterHasAttributes(folder.subfolders?.data, [
|
||||
"attributes",
|
||||
]).map(({ attributes }) => ({
|
||||
slug: attributes.slug,
|
||||
href: `/contents/folder/${attributes.slug}`,
|
||||
translations: filterHasAttributes(attributes.titles, ["language.data.attributes.code"]).map(
|
||||
(translation) => ({
|
||||
title: translation.title,
|
||||
language: translation.language.data.attributes.code,
|
||||
})
|
||||
),
|
||||
fallback: { title: prettySlug(attributes.slug) },
|
||||
}));
|
||||
|
||||
const contents: Props["contents"] = filterHasAttributes(folder.contents?.data, [
|
||||
"attributes",
|
||||
]).map(({ attributes }) => ({
|
||||
slug: attributes.slug,
|
||||
href: `/contents/${attributes.slug}`,
|
||||
translations: filterHasAttributes(attributes.translations, [
|
||||
"language.data.attributes.code",
|
||||
]).map((translation) => ({
|
||||
pre_title: translation.pre_title ?? undefined,
|
||||
title: translation.title,
|
||||
subtitle: translation.subtitle ?? undefined,
|
||||
language: translation.language.data.attributes.code,
|
||||
})),
|
||||
fallback: { title: prettySlug(attributes.slug) },
|
||||
thumbnail: attributes.thumbnail?.data?.attributes ?? undefined,
|
||||
topChips: attributes.type?.data?.attributes
|
||||
? [formatContentType(attributes.type.data.attributes.slug)]
|
||||
: undefined,
|
||||
bottomChips: filterHasAttributes(attributes.categories?.data, ["attributes"]).map((category) =>
|
||||
formatCategory(category.attributes.slug)
|
||||
),
|
||||
}));
|
||||
|
||||
const props: Props = {
|
||||
openGraph: getOpenGraph(format, title),
|
||||
folder,
|
||||
subfolders,
|
||||
contents,
|
||||
path: getRecursiveParentFolderPreview(folder),
|
||||
};
|
||||
return {
|
||||
props: props,
|
||||
props: JSON.parse(JSON.stringify(props)),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue