151 lines
6.1 KiB
TypeScript
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,
|
|
};
|
|
};
|