Added functionality to trending page

main
Guus van Meerveld 2 years ago
parent ee1bab191f
commit 9c1a95e3ff
Signed by: Guusvanmeerveld
GPG Key ID: 2BA7D7912771966E

@ -21,7 +21,8 @@
"next": "^12.1.0",
"next-seo": "^5.1.0",
"react": "^17.0.2",
"react-dom": "^17.0.2"
"react-dom": "^17.0.2",
"react-query": "^3.34.16"
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^3.2.0",

@ -65,7 +65,7 @@ const Navbar: FC = () => {
pages={pages}
/>
<Box sx={{ flexGrow: 1 }}>
<AppBar position="static">
<AppBar position="static" enableColorOnDark>
<Toolbar>
<IconButton
size="large"

@ -11,7 +11,7 @@ const VideoGrid: FC<{ videos: VideoModel[] }> = ({ videos }) => {
<Grid
container
spacing={{ xs: 2, md: 3 }}
columns={{ xs: 3, sm: 9, md: 12 }}
columns={{ xs: 3, sm: 6, md: 9, lg: 12 }}
>
{videos.map((video) => (
<Grid item key={video.id} xs={3}>

@ -1,33 +1,59 @@
import Link from "next/link";
import { useRouter } from "next/router";
import { FC } from "react";
import { useQuery } from "react-query";
import Avatar from "@mui/material/Avatar";
import Box from "@mui/material/Box";
import Card from "@mui/material/Card";
import CardActionArea from "@mui/material/CardActionArea";
import CardContent from "@mui/material/CardContent";
import CardMedia from "@mui/material/CardMedia";
import Tooltip from "@mui/material/Tooltip";
import Typography from "@mui/material/Typography";
import { Video } from "@interfaces/video";
import { abbreviateNumber } from "@src/utils";
import { Channel, Quality } from "@interfaces/channel";
import { Video as VideoModel } from "@interfaces/video";
const Video: FC<Video> = ({ thumbnail, title, description, id }) => {
const Video: FC<VideoModel> = ({
thumbnail,
title,
id,
author,
views,
published
}) => {
const router = useRouter();
return (
<Card sx={{ display: "inline-block" }}>
<CardActionArea onClick={() => router.push(`/video?id=${id}`)}>
<Card sx={{ display: "inline-block" }}>
<CardActionArea onClick={() => router.push(`/watch?v=${id}`)}>
<CardMedia
height="270"
component="img"
image={thumbnail}
alt="video thumbnail"
/>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
{title}
</Typography>
<Typography variant="body2" color="text.secondary">
{description}
<Tooltip title={title}>
<Typography gutterBottom variant="h6" component="div">
{title}
</Typography>
</Tooltip>
<Link passHref href={`/channel/${author.id}`}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Avatar sx={{ mr: 2 }} alt={author.name} />
<Typography color="text.secondary" variant="subtitle1">
{author.name}
</Typography>
</Box>
</Link>
<Typography sx={{ mt: 2 }} color="text.secondary" variant="body2">
{abbreviateNumber(views)} Views Published {published.text}
</Typography>
</CardContent>
</CardActionArea>

@ -0,0 +1,45 @@
import { Video } from "@interfaces/video";
export interface Channel {
author: string;
authorId: string;
authorUrl: string;
authorBanners: AuthorBanner[];
authorThumbnails: AuthorBanner[];
subCount: number;
totalViews: number;
joined: number;
autoGenerated: boolean;
isFamilyFriendly: boolean;
description: string;
descriptionHtml: string;
allowedRegions: string[];
latestVideos: Video[];
relatedChannels: RelatedChannel[];
}
export interface AuthorBanner {
url: string;
width: number;
height: number;
quality?: Quality;
}
export enum Quality {
Default = "default",
End = "end",
High = "high",
Maxres = "maxres",
Maxresdefault = "maxresdefault",
Medium = "medium",
Middle = "middle",
Sddefault = "sddefault",
Start = "start"
}
export interface RelatedChannel {
author: string;
authorId: string;
authorUrl: string;
authorThumbnails: AuthorBanner[];
}

@ -1,6 +1,46 @@
export interface Video {
thumbnail: string;
title: string;
description: string;
description: {
text: string;
html: string;
};
id: string;
author: {
name: string;
id: string;
url: string;
};
views: number;
published: {
time: number;
text: string;
};
length: number;
}
export interface Trending {
type: string;
title: string;
videoId: string;
author: string;
authorId: string;
authorUrl: string;
videoThumbnails: VideoThumbnail[];
description: string;
descriptionHtml: string;
viewCount: number;
published: number;
publishedText: string;
lengthSeconds: number;
liveNow: boolean;
premium: boolean;
isUpcoming: boolean;
}
interface VideoThumbnail {
quality: string;
url: string;
width: number;
height: number;
}

@ -5,6 +5,9 @@ import type { AppProps } from "next/app";
import { useMemo } from "react";
import { QueryClient, QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import createCache from "@emotion/cache";
import { CacheProvider } from "@emotion/react";
@ -16,6 +19,8 @@ import createTheme from "@src/theme";
import "@styles/globals.sass";
const queryClient = new QueryClient();
const cache = createCache({ key: "next" });
const App = ({ Component, pageProps }: AppProps) => {
@ -25,11 +30,14 @@ const App = ({ Component, pageProps }: AppProps) => {
return (
<CacheProvider value={cache}>
<ThemeProvider theme={theme}>
<CssBaseline />
<DefaultSeo {...SEO} />
<Component {...pageProps} />
</ThemeProvider>
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools initialIsOpen={true} />
<ThemeProvider theme={theme}>
<CssBaseline />
<DefaultSeo {...SEO} />
<Component {...pageProps} />
</ThemeProvider>
</QueryClientProvider>
</CacheProvider>
);
};

@ -1,35 +1,40 @@
import { NextPage } from "next";
import { NextSeo } from "next-seo";
import { useQuery } from "react-query";
import Box from "@mui/material/Box";
import { Video as VideoModel } from "@interfaces/video";
import { Typography } from "@mui/material";
import Layout from "@components/Layout";
import Grid from "@components/Video/Grid";
import {
Trending as TrendingModel,
Video as VideoModel
} from "@interfaces/video";
const exampleVideo = {
title: "Yeet",
description: "Yarr",
id: Math.random().toString(),
thumbnail: "https://invidious.privacy.gd/vi/qF1DTK4U1AM/maxresdefault.jpg"
};
import { trendingToVideo } from "@utils/conversions";
const videos: VideoModel[] = [
exampleVideo,
exampleVideo,
exampleVideo,
exampleVideo,
exampleVideo
];
import Layout from "@components/Layout";
import Grid from "@components/Video/Grid";
const Trending: NextPage = () => {
const { isLoading, error, data } = useQuery<TrendingModel[]>(
"trendingData",
() =>
fetch("https://invidious.privacy.gd/api/v1/trending").then((res) =>
res.json()
)
);
return (
<>
<NextSeo title="Trending" />
<Layout>
<Box sx={{ padding: { xs: 2, md: 5 } }}>
<Grid videos={videos} />
{isLoading && <Typography variant="h3">Loading</Typography>}
{!isLoading && !error && data && (
<Grid videos={data.map(trendingToVideo)} />
)}
</Box>
</Layout>
</>

@ -0,0 +1,26 @@
import { Trending, Video } from "@interfaces/video";
export const trendingToVideo = (item: Trending): Video => {
return {
title: item.title,
description: {
text: item.description,
html: item.descriptionHtml
},
id: item.videoId,
author: {
name: item.author,
id: item.authorId,
url: item.authorUrl
},
length: item.lengthSeconds,
published: {
time: item.published,
text: item.publishedText
},
views: item.viewCount,
thumbnail: item.videoThumbnails.find(
(thumbnail) => thumbnail.quality == "maxresdefault"
)?.url as string
};
};

@ -0,0 +1,14 @@
export const abbreviateNumber = (value: number): string => {
const suffixes = ["", "K", "M", "B", "T"];
let suffixNum = 0;
while (value >= 1000) {
value /= 1000;
suffixNum++;
}
value = parseInt(value.toPrecision(3));
return `${value}${suffixes[suffixNum]}`;
};

@ -20,6 +20,8 @@
"@styles/*": ["styles/*"],
"@components/*": ["components/*"],
"@interfaces/*": ["interfaces/*"],
"@utils/*": ["utils/*"],
"@src/*": ["./*"]
}
},

@ -215,6 +215,13 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.12.5", "@babel/runtime@^7.6.2":
version "7.17.8"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.8.tgz#3e56e4aff81befa55ac3ac6a0967349fd1c5bca2"
integrity sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.7":
version "7.17.7"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.7.tgz#a5f3328dc41ff39d803f311cfe17703418cf9825"
@ -929,6 +936,11 @@ balanced-match@^2.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9"
integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==
big-integer@^1.6.16:
version "1.6.51"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686"
integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
@ -949,6 +961,20 @@ braces@^3.0.1, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
broadcast-channel@^3.4.1:
version "3.7.0"
resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937"
integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==
dependencies:
"@babel/runtime" "^7.7.2"
detect-node "^2.1.0"
js-sha3 "0.8.0"
microseconds "0.2.0"
nano-time "1.0.0"
oblivious-set "1.0.0"
rimraf "3.0.2"
unload "2.2.0"
browserslist@^4.17.5:
version "4.20.2"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.2.tgz#567b41508757ecd904dab4d1c646c612cd3d4f88"
@ -1203,6 +1229,11 @@ define-properties@^1.1.3:
dependencies:
object-keys "^1.0.12"
detect-node@^2.0.4, detect-node@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
@ -2088,6 +2119,11 @@ javascript-natural-sort@0.7.1:
resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59"
integrity sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=
js-sha3@0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@ -2244,6 +2280,14 @@ map-obj@^4.0.0:
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.2.1.tgz#e4ea399dbc979ae735c83c863dd31bdf364277b7"
integrity sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==
match-sorter@^6.0.2:
version "6.3.1"
resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.1.tgz#98cc37fda756093424ddf3cbc62bfe9c75b92bda"
integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==
dependencies:
"@babel/runtime" "^7.12.5"
remove-accents "0.4.2"
mathml-tag-names@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3"
@ -2280,6 +2324,11 @@ micromatch@^4.0.4:
braces "^3.0.1"
picomatch "^2.2.3"
microseconds@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39"
integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==
min-indent@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
@ -2328,6 +2377,13 @@ ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
nano-time@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef"
integrity sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=
dependencies:
big-integer "^1.6.16"
nanoid@^3.1.30, nanoid@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35"
@ -2466,6 +2522,11 @@ object.values@^1.1.5:
define-properties "^1.1.3"
es-abstract "^1.19.1"
oblivious-set@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566"
integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@ -2703,6 +2764,15 @@ react-is@^17.0.2:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-query@^3.34.16:
version "3.34.16"
resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.34.16.tgz#279ea180bcaeaec49c7864b29d1711ee9f152594"
integrity sha512-7FvBvjgEM4YQ8nPfmAr+lJfbW95uyW/TVjFoi2GwCkF33/S8ajx45tuPHPFGWs4qYwPy1mzwxD4IQfpUDrefNQ==
dependencies:
"@babel/runtime" "^7.5.5"
broadcast-channel "^3.4.1"
match-sorter "^6.0.2"
react-transition-group@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
@ -2773,6 +2843,11 @@ regexpp@^3.2.0:
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
remove-accents@0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
integrity sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U=
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
@ -2818,7 +2893,7 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
rimraf@^3.0.2:
rimraf@3.0.2, rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
@ -3246,6 +3321,14 @@ unbox-primitive@^1.0.1:
has-symbols "^1.0.2"
which-boxed-primitive "^1.0.2"
unload@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7"
integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==
dependencies:
"@babel/runtime" "^7.6.2"
detect-node "^2.0.4"
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"

Loading…
Cancel
Save