151 lines
6.1 KiB
TypeScript

import { GetStaticProps } from "next";
import { useMemo, useState } from "react";
import { useBoolean } from "usehooks-ts";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { SmartList } from "components/SmartList";
import { Icon } from "components/Ico";
import { Switch } from "components/Inputs/Switch";
import { TextInput } from "components/Inputs/TextInput";
import { WithLabel } from "components/Inputs/WithLabel";
import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { ReturnButton } from "components/PanelComponents/ReturnButton";
import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { SubPanel } from "components/Containers/SubPanel";
import { PreviewCard } from "components/PreviewCard";
import { GetVideosPreviewQuery } from "graphql/generated";
import { getReadySdk } from "graphql/sdk";
import { filterHasAttributes } from "helpers/others";
import { getVideoThumbnailURL } from "helpers/videos";
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { getOpenGraph } from "helpers/openGraph";
import { compareDate } from "helpers/date";
import { HorizontalLine } from "components/HorizontalLine";
import { cIf } from "helpers/className";
import { getLangui } from "graphql/fetchLocalData";
import { atoms } from "contexts/atoms";
import { useAtomGetter } from "helpers/atoms";
/*
* ╭─────────────╮
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
*/
const DEFAULT_FILTERS_STATE = {
searchName: "",
};
/*
* ╭────────╮
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
*/
interface Props extends AppLayoutRequired {
videos: NonNullable<GetVideosPreviewQuery["videos"]>["data"];
}
const Videos = ({ videos, ...otherProps }: Props): JSX.Element => {
const langui = useAtomGetter(atoms.localData.langui);
const hoverable = useDeviceSupportsHover();
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = useBoolean(true);
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
const subPanel = useMemo(
() => (
<SubPanel>
<ReturnButton
href="/archives/"
title={"Archives"}
displayOnlyOn={"3ColumnsLayout"}
className="mb-10"
/>
<PanelHeader icon={Icon.Movie} title="Videos" description={langui.archives_description} />
<HorizontalLine />
<TextInput
className="mb-6 w-full"
placeholder={langui.search_title ?? "Search title..."}
value={searchName}
onChange={setSearchName}
/>
{hoverable && (
<WithLabel label={langui.always_show_info}>
<Switch value={keepInfoVisible} onClick={toggleKeepInfoVisible} />
</WithLabel>
)}
</SubPanel>
),
[hoverable, keepInfoVisible, langui, searchName, toggleKeepInfoVisible]
);
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
<SmartList
items={filterHasAttributes(videos, ["id", "attributes"] as const)}
getItemId={(item) => item.id}
renderItem={({ item }) => (
<PreviewCard
href={`/archives/videos/v/${item.attributes.uid}`}
title={item.attributes.title}
thumbnail={getVideoThumbnailURL(item.attributes.uid)}
thumbnailAspectRatio="16/9"
thumbnailForceAspectRatio
keepInfoVisible={keepInfoVisible}
metadata={{
releaseDate: item.attributes.published_date,
views: item.attributes.views,
author: item.attributes.channel?.data?.attributes?.title,
position: "Top",
}}
hoverlay={{
__typename: "Video",
duration: item.attributes.duration,
}}
/>
)}
className={cIf(
isContentPanelAtLeast4xl,
"grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
"grid-cols-2 gap-x-3 gap-y-5"
)}
paginationItemPerPage={25}
searchingTerm={searchName}
searchingBy={(item) => item.attributes.title}
/>
</ContentPanel>
),
[isContentPanelAtLeast4xl, keepInfoVisible, searchName, videos]
);
return <AppLayout subPanel={subPanel} contentPanel={contentPanel} {...otherProps} />;
};
export default Videos;
/*
* ╭──────────────────────╮
* ───────────────────────────────────╯ NEXT DATA FETCHING ╰──────────────────────────────────────
*/
export const getStaticProps: GetStaticProps = async (context) => {
const sdk = getReadySdk();
const langui = getLangui(context.locale);
const videos = await sdk.getVideosPreview();
if (!videos.videos) return { notFound: true };
videos.videos.data
.sort((a, b) => compareDate(a.attributes?.published_date, b.attributes?.published_date))
.reverse();
const props: Props = {
videos: videos.videos.data,
openGraph: getOpenGraph(langui, langui.videos ?? "Videos"),
};
return {
props: props,
};
};