From e3c2e4da1d71d7b4eb167a5a199e12913244398c Mon Sep 17 00:00:00 2001 From: DrMint Date: Fri, 1 Apr 2022 20:47:42 +0200 Subject: [PATCH] Added basic video functionnality --- next.config.js | 2 +- src/components/LanguageSwitcher.tsx | 6 +- src/components/Panels/MainPanel.tsx | 6 - src/components/Videos/VideoPreview.tsx | 80 ++++++++ src/graphql/operations/getVideo.graphql | 55 +++++ .../operations/getVideosPreview.graphql | 32 +++ src/graphql/operations/getVideosSlugs.graphql | 10 + src/pages/archives/index.tsx | 2 + src/pages/archives/videos/[uid].tsx | 193 ++++++++++++++++++ src/pages/archives/videos/index.tsx | 73 +++++++ src/queries/helpers.ts | 39 ++++ 11 files changed, 488 insertions(+), 10 deletions(-) create mode 100644 src/components/Videos/VideoPreview.tsx create mode 100644 src/graphql/operations/getVideo.graphql create mode 100644 src/graphql/operations/getVideosPreview.graphql create mode 100644 src/graphql/operations/getVideosSlugs.graphql create mode 100644 src/pages/archives/videos/[uid].tsx create mode 100644 src/pages/archives/videos/index.tsx diff --git a/next.config.js b/next.config.js index 7e0539c..72deeb1 100644 --- a/next.config.js +++ b/next.config.js @@ -14,7 +14,7 @@ module.exports = { defaultLocale: "en", }, images: { - domains: ["img.accords-library.com"], + domains: ["img.accords-library.com", "watch.accords-library.com"], }, serverRuntimeConfig: { locales: locales, diff --git a/src/components/LanguageSwitcher.tsx b/src/components/LanguageSwitcher.tsx index 9b9ac47..8f852a0 100644 --- a/src/components/LanguageSwitcher.tsx +++ b/src/components/LanguageSwitcher.tsx @@ -3,7 +3,7 @@ import { AppStaticProps } from "queries/getAppStaticProps"; import { prettyLanguage } from "queries/helpers"; import Button from "./Button"; -type HorizontalLineProps = { +type Props = { className?: string; locales: (string | undefined)[]; languages: AppStaticProps["languages"]; @@ -11,8 +11,8 @@ type HorizontalLineProps = { href?: string; }; -export default function HorizontalLine( - props: HorizontalLineProps +export default function LanguageSwitcher( + props: Props ): JSX.Element { const { locales, langui, href } = props; const router = useRouter(); diff --git a/src/components/Panels/MainPanel.tsx b/src/components/Panels/MainPanel.tsx index 90b04ed..3fd328b 100644 --- a/src/components/Panels/MainPanel.tsx +++ b/src/components/Panels/MainPanel.tsx @@ -207,20 +207,14 @@ export default function MainPanel(props: MainPanelProps): JSX.Element { onClick={() => appLayout.setMainPanelOpen(false)} /> - {/* - appLayout.setMainPanelOpen(false)} /> - - */} - ["data"][number]["attributes"], + null | undefined + >; +}; + +export default function PostPreview(props: Props): JSX.Element { + const { video } = props; + + return ( + +
+
+ {video.title} +
+ + play_circle_outline + +
+
+ {prettyDuration(video.duration)} +
+
+
+
+

+ + event + + {prettyDate(video.published_date)} +

+

+ + visibility + + {prettyShortenNumber(video.views)} +

+ {video.channel?.data?.attributes && ( +

+ + person + + {video.channel.data.attributes.title} +

+ )} +
+ +
+

{video.title}

+
+
+ {video.categories?.data.map((category) => ( + + {category.attributes?.short} + + ))} +
+
+
+ + ); +} diff --git a/src/graphql/operations/getVideo.graphql b/src/graphql/operations/getVideo.graphql new file mode 100644 index 0000000..aaa2b91 --- /dev/null +++ b/src/graphql/operations/getVideo.graphql @@ -0,0 +1,55 @@ +query getVideo($uid: String) { + videos(filters: { uid: { eq: $uid } }) { + data { + id + attributes { + uid + title + description + published_date { + year + month + day + } + channel { + data { + attributes { + uid + title + subscribers + } + } + } + categories { + data { + id + attributes { + short + } + } + } + views + likes + source + audio_languages { + data { + id + attributes { + code + } + } + } + sub_languages { + data { + id + attributes { + code + } + } + } + width + height + } + } + } +} diff --git a/src/graphql/operations/getVideosPreview.graphql b/src/graphql/operations/getVideosPreview.graphql new file mode 100644 index 0000000..0cb41bc --- /dev/null +++ b/src/graphql/operations/getVideosPreview.graphql @@ -0,0 +1,32 @@ +query getVideosPreview { + videos(pagination: { limit: -1 }) { + data { + id + attributes { + uid + title + views + duration + categories { + data { + id + attributes { + short + } + } + } + published_date { + ...datePicker + } + channel { + data { + attributes { + uid + title + } + } + } + } + } + } +} diff --git a/src/graphql/operations/getVideosSlugs.graphql b/src/graphql/operations/getVideosSlugs.graphql new file mode 100644 index 0000000..c252c38 --- /dev/null +++ b/src/graphql/operations/getVideosSlugs.graphql @@ -0,0 +1,10 @@ +query getVideosSlugs { + videos(pagination: {limit:-1}) { + data { + id + attributes { + uid + } + } + } +} \ No newline at end of file diff --git a/src/pages/archives/index.tsx b/src/pages/archives/index.tsx index 61c86f0..f6d0213 100644 --- a/src/pages/archives/index.tsx +++ b/src/pages/archives/index.tsx @@ -1,4 +1,5 @@ import AppLayout from "components/AppLayout"; +import NavOption from "components/PanelComponents/NavOption"; import PanelHeader from "components/PanelComponents/PanelHeader"; import SubPanel from "components/Panels/SubPanel"; import { GetStaticPropsContext } from "next"; @@ -15,6 +16,7 @@ export default function Archives(props: ArchivesProps): JSX.Element { title={langui.archives} description={langui.archives_description} /> + ); return ( diff --git a/src/pages/archives/videos/[uid].tsx b/src/pages/archives/videos/[uid].tsx new file mode 100644 index 0000000..87eacd8 --- /dev/null +++ b/src/pages/archives/videos/[uid].tsx @@ -0,0 +1,193 @@ +import AppLayout from "components/AppLayout"; +import Button from "components/Button"; +import HorizontalLine from "components/HorizontalLine"; +import InsetBox from "components/InsetBox"; +import NavOption from "components/PanelComponents/NavOption"; +import ReturnButton, { + ReturnButtonType, +} from "components/PanelComponents/ReturnButton"; +import ContentPanel, { + ContentPanelWidthSizes, +} from "components/Panels/ContentPanel"; +import SubPanel from "components/Panels/SubPanel"; +import { useAppLayout } from "contexts/AppLayoutContext"; +import { GetVideoQuery } from "graphql/generated"; +import { getReadySdk } from "graphql/sdk"; +import { useMediaMobile } from "hooks/useMediaQuery"; +import { + GetStaticPathsContext, + GetStaticPathsResult, + GetStaticPropsContext, +} from "next"; +import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; +import { getVideoFile, prettyDate, prettyShortenNumber } from "queries/helpers"; + +interface Props extends AppStaticProps { + video: Exclude< + Exclude< + GetVideoQuery["videos"], + null | undefined + >["data"][number]["attributes"], + null | undefined + >; +} + +export default function Video(props: Props): JSX.Element { + const { langui, video } = props; + const isMobile = useMediaMobile(); + const appLayout = useAppLayout(); + const subPanel = ( + + + + + + appLayout.setSubPanelOpen(false)} + /> + + appLayout.setSubPanelOpen(false)} + /> + + appLayout.setSubPanelOpen(false)} + /> + + ); + + const contentPanel = ( + + + +
+
+ +
+

{video.title}

+
+

+ + event + + {prettyDate(video.published_date)} +

+

+ + visibility + + {isMobile + ? prettyShortenNumber(video.views) + : video.views.toLocaleString()} +

+ {video.channel?.data?.attributes && ( +

+ + thumb_up + + {isMobile + ? prettyShortenNumber(video.likes) + : video.likes.toLocaleString()} +

+ )} + +
+
+
+ + {video.channel?.data?.attributes && ( + +
+

{"Channel"}

+
+ + +

+ {video.channel.data.attributes.subscribers.toLocaleString()}{" "} + subscribers +

+
+
+
+ )} + + +
+

{"Description"}

+

{video.description}

+
+
+
+
+ ); + return ( + + ); +} + +export async function getStaticProps( + context: GetStaticPropsContext +): Promise<{ notFound: boolean } | { props: Props }> { + const sdk = getReadySdk(); + const videos = await sdk.getVideo({ + uid: context.params?.uid ? context.params.uid.toString() : "", + }); + if (!videos.videos?.data[0].attributes) return { notFound: true }; + const props: Props = { + ...(await getAppStaticProps(context)), + video: videos.videos.data[0].attributes, + }; + return { + props: props, + }; +} + +export async function getStaticPaths( + context: GetStaticPathsContext +): Promise { + const sdk = getReadySdk(); + const videos = await sdk.getVideo(); + const paths: GetStaticPathsResult["paths"] = []; + if (videos.videos?.data) + videos.videos.data.map((video) => { + context.locales?.map((local) => { + if (video.attributes) + paths.push({ params: { uid: video.attributes.uid }, locale: local }); + }); + }); + return { + paths, + fallback: "blocking", + }; +} diff --git a/src/pages/archives/videos/index.tsx b/src/pages/archives/videos/index.tsx new file mode 100644 index 0000000..7da5a60 --- /dev/null +++ b/src/pages/archives/videos/index.tsx @@ -0,0 +1,73 @@ +import AppLayout from "components/AppLayout"; +import HorizontalLine from "components/HorizontalLine"; +import PanelHeader from "components/PanelComponents/PanelHeader"; +import ReturnButton, { + ReturnButtonType, +} from "components/PanelComponents/ReturnButton"; +import ContentPanel, { + ContentPanelWidthSizes, +} from "components/Panels/ContentPanel"; +import SubPanel from "components/Panels/SubPanel"; +import VideoPreview from "components/Videos/VideoPreview"; +import { GetVideosPreviewQuery } from "graphql/generated"; +import { getReadySdk } from "graphql/sdk"; +import { GetStaticPropsContext } from "next"; +import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; + +interface Props extends AppStaticProps { + videos: Exclude["data"]; +} + +export default function Videos(props: Props): JSX.Element { + const { langui, videos } = props; + const subPanel = ( + + + + + + ); + + const contentPanel = ( + +
+ {videos.map((video) => ( + <>{video.attributes && } + ))} +
+
+ ); + return ( + + ); +} + +export async function getStaticProps( + context: GetStaticPropsContext +): Promise<{ notFound: boolean } | { props: Props }> { + const sdk = getReadySdk(); + const videos = await sdk.getVideosPreview(); + if (!videos.videos) return { notFound: true }; + const props: Props = { + ...(await getAppStaticProps(context)), + videos: videos.videos.data, + }; + return { + props: props, + }; +} diff --git a/src/queries/helpers.ts b/src/queries/helpers.ts index c63905e..86d0a07 100644 --- a/src/queries/helpers.ts +++ b/src/queries/helpers.ts @@ -229,6 +229,37 @@ export function prettyItemSubType( /* eslint-enable @typescript-eslint/no-explicit-any */ } +export function prettyShortenNumber(number: number): string { + if (number > 1000000) { + return number.toLocaleString(undefined, { + maximumSignificantDigits: 3, + }); + } else if (number > 1000) { + return (number / 1000).toLocaleString(undefined, { + maximumSignificantDigits: 2, + }) + "K"; + } + return number.toLocaleString(); +} + +export function prettyDuration(seconds: number): string { + let hours = 0; + let minutes = 0; + while (seconds > 60) { + minutes += 1; + seconds -= 60; + } + while (minutes > 60) { + hours += 1; + minutes -= 60; + } + let result = ""; + if (hours) result += hours.toString().padStart(2, "0") + ":"; + result += minutes.toString().padStart(2, "0") + ":"; + result += seconds.toString().padStart(2, "0"); + return result; +} + export function prettyLanguage( code: string, languages: AppStaticProps["languages"] @@ -417,3 +448,11 @@ export function getLocalesFromLanguages( ? languages.map((language) => language?.language?.data?.attributes?.code) : []; } + +export function getVideoThumbnailURL(uid: string):string { + return `${process.env.NEXT_PUBLIC_URL_WATCH}/videos/${uid}.webp`; +} + +export function getVideoFile(uid: string):string { + return `${process.env.NEXT_PUBLIC_URL_WATCH}/videos/${uid}.mp4`; +} \ No newline at end of file