Simplified some DTO

This commit is contained in:
DrMint 2023-08-17 12:52:40 +02:00
parent 7efa43a630
commit da916f898a
3 changed files with 150 additions and 85 deletions

View File

@ -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,

View File

@ -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)),
};
};

View File

@ -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)),
};
};