Added categories to search results #9

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

@ -32,9 +32,10 @@ export const drawerWidth = 240;
const Navbar: FC = () => { const Navbar: FC = () => {
const [drawerIsOpen, setDrawerState] = useState(false); const [drawerIsOpen, setDrawerState] = useState(false);
const router = useRouter(); const router = useRouter();
const query = router.query["search_query"]; const [search, setSearch] = useState<string | undefined>();
const toggleDrawer = const toggleDrawer =
(open: boolean) => (event: React.KeyboardEvent | React.MouseEvent) => { (open: boolean) => (event: React.KeyboardEvent | React.MouseEvent) => {
@ -141,9 +142,20 @@ const Navbar: FC = () => {
<SearchIconWrapper> <SearchIconWrapper>
<SearchIcon /> <SearchIcon />
</SearchIconWrapper> </SearchIconWrapper>
<form action={`${router.basePath}/results`} method="get"> <form
onSubmit={(e) => {
e.preventDefault();
router.push({
pathname: "/results",
query: { search_query: search }
});
}}
method="get"
>
<StyledInputBase <StyledInputBase
defaultValue={query ?? ""} onChange={(e) => setSearch(e.target.value)}
value={search}
name="search_query" name="search_query"
placeholder="Search…" placeholder="Search…"
inputProps={{ "aria-label": "search" }} inputProps={{ "aria-label": "search" }}

@ -7,7 +7,9 @@ import Box from "@mui/material/Box";
import CircularProgress from "@mui/material/CircularProgress"; import CircularProgress from "@mui/material/CircularProgress";
import Grid from "@mui/material/Grid"; import Grid from "@mui/material/Grid";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
import Tooltip from "@mui/material/Tooltip";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { red } from "@mui/material/colors";
import { useTheme } from "@mui/material/styles"; import { useTheme } from "@mui/material/styles";
import { abbreviateNumber } from "@src/utils/"; import { abbreviateNumber } from "@src/utils/";
@ -28,7 +30,22 @@ const Video: FC<{ video: VideoModel }> = ({ video }) => {
return ( return (
<Paper sx={{ my: 2 }}> <Paper sx={{ my: 2 }}>
<Grid container spacing={0}> <Grid container spacing={0}>
<Grid item md={4}> <Grid item md={4} sx={{ position: "relative" }}>
{video.live && (
<Box
sx={{
backgroundColor: red[600],
position: "absolute",
right: 5,
top: 5,
p: "3px",
borderRadius: "2px",
textTransform: "uppercase"
}}
>
Live
</Box>
)}
{/* eslint-disable-next-line @next/next/no-img-element */} {/* eslint-disable-next-line @next/next/no-img-element */}
<img <img
style={{ style={{
@ -45,9 +62,11 @@ const Video: FC<{ video: VideoModel }> = ({ video }) => {
<Grid item md={8} sx={{ padding: 3, width: "100%" }}> <Grid item md={8} sx={{ padding: 3, width: "100%" }}>
<Link href={{ pathname: "/watch", query: { v: video.id } }}> <Link href={{ pathname: "/watch", query: { v: video.id } }}>
<a> <a>
<Typography gutterBottom noWrap variant="h5"> <Tooltip title={video.title}>
{video.title} <Typography gutterBottom noWrap variant="h5">
</Typography> {video.title}
</Typography>
</Tooltip>
</a> </a>
</Link> </Link>
<Typography <Typography
@ -55,8 +74,21 @@ const Video: FC<{ video: VideoModel }> = ({ video }) => {
variant="subtitle1" variant="subtitle1"
color={theme.palette.text.secondary} color={theme.palette.text.secondary}
> >
{abbreviateNumber(video.views)} Views Published{" "} {!(video.live || video.upcoming) && (
{video.published.text} <>
{abbreviateNumber(video.views)} Views Published{" "}
{video.published.text}
</>
)}
{video.live && <>🔴 Live now</>}
{video.upcoming && video.premiereTimestamp && (
<>
Premiering on{" "}
{new Date(video.premiereTimestamp * 1000).toLocaleDateString()}{" "}
at{" "}
{new Date(video.premiereTimestamp * 1000).toLocaleTimeString()}
</>
)}
</Typography> </Typography>
<Typography <Typography
gutterBottom gutterBottom

@ -75,8 +75,12 @@ const Video: FC<VideoModel> = (video) => {
color={theme.palette.text.secondary} color={theme.palette.text.secondary}
variant="body2" variant="body2"
> >
{abbreviateNumber(video.views)} Views Published{" "} {!(video.live || video.upcoming) && (
{video.published.text} <>
{abbreviateNumber(video.views)} Views Published{" "}
{video.published.text}
</>
)}
</Typography> </Typography>
</CardContent> </CardContent>
</CardActionArea> </CardActionArea>

@ -31,6 +31,8 @@ export interface VideoResult extends Results {
published: number; published: number;
publishedText: string; publishedText: string;
lengthSeconds: number; lengthSeconds: number;
isUpcoming: boolean;
premiereTimestamp?: number;
liveNow: boolean; liveNow: boolean;
premium: boolean; premium: boolean;
} }

@ -17,6 +17,7 @@ interface Trending {
liveNow: boolean; liveNow: boolean;
premium: boolean; premium: boolean;
isUpcoming: boolean; isUpcoming: boolean;
premiereTimestamp?: number;
} }
export default Trending; export default Trending;

@ -32,6 +32,8 @@ interface Video {
}; };
subscriptions?: string; subscriptions?: string;
rating?: number; rating?: number;
upcoming?: boolean;
premiereTimestamp?: number;
premiered?: Date; premiered?: Date;
recommendedVideos?: RecommendedVideo[]; recommendedVideos?: RecommendedVideo[];
adaptiveFormats?: AdaptiveFormat[]; adaptiveFormats?: AdaptiveFormat[];

@ -4,13 +4,20 @@ import { NextPage } from "next";
import { NextSeo } from "next-seo"; import { NextSeo } from "next-seo";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { FC, useState } from "react";
import { useQuery } from "react-query"; import { useQuery } from "react-query";
import axios from "axios"; import axios from "axios";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Container from "@mui/material/Container"; import Container from "@mui/material/Container";
import Divider from "@mui/material/Divider"; import Divider from "@mui/material/Divider";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { useTheme } from "@mui/material/styles";
import Add from "@mui/icons-material/Add";
import Result, { import Result, {
CategoryResult, CategoryResult,
@ -49,6 +56,50 @@ const Results: NextPage = () => {
: undefined : undefined
); );
const Category: FC<{ category: CategoryResult }> = ({ category }) => {
const initialCount = 3;
const [count, setCount] = useState(initialCount);
const shownVideos = category.contents.slice(0, count);
return (
<>
<Typography variant="h5">{category.title}</Typography>
{shownVideos.map((video, i) => (
<Video video={apiToVideo(video)} key={i} />
))}
{category.contents.length > initialCount && (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
mt: 4
}}
>
<Button
variant="text"
onClick={() =>
setCount(
count > initialCount ? initialCount : category.contents.length
)
}
>
<Add />
Show {count > initialCount ? "less" : "more"} (
{category.contents.length - initialCount})
</Button>
</Box>
)}
<Divider sx={{ my: 4 }} />
</>
);
};
if (!router.isReady || isLoading) if (!router.isReady || isLoading)
return ( return (
<> <>
@ -69,7 +120,9 @@ const Results: NextPage = () => {
| VideoResult[] | VideoResult[]
| undefined; | undefined;
// const categories = data?.filter() const categories = data?.filter((result) => result.type == "category") as
| CategoryResult[]
| undefined;
return ( return (
<> <>
@ -85,6 +138,13 @@ const Results: NextPage = () => {
<Divider sx={{ my: 4 }} /> <Divider sx={{ my: 4 }} />
</> </>
)} )}
{categories && categories.length != 0 && (
<>
{categories.map((category, i) => (
<Category key={i} category={category} />
))}
</>
)}
{videos && videos.length != 0 && ( {videos && videos.length != 0 && (
<> <>
<Typography variant="h5">Videos</Typography> <Typography variant="h5">Videos</Typography>

@ -1,4 +1,4 @@
import { NextPage } from "next"; import { GetStaticProps, NextPage } from "next";
import { NextSeo } from "next-seo"; import { NextSeo } from "next-seo";
import { useState } from "react"; import { useState } from "react";
@ -21,7 +21,33 @@ import Layout from "@components/Layout";
import Loading from "@components/Loading"; import Loading from "@components/Loading";
import Grid from "@components/Video/Grid"; import Grid from "@components/Video/Grid";
const Trending: NextPage = () => { const fetchTrending = (server: string, category = "") =>
axios
.get(`https://${server}/api/v1/trending`, {
params: {
fields: [
"title",
"description",
"descriptionHtml",
"videoId",
"author",
"authorId",
"authorUrl",
"lengthSeconds",
"published",
"publishedText",
"liveNow",
"premium",
"isUpcoming",
"viewCount",
"videoThumbnails"
].join(","),
type: category
}
})
.then((res) => res.data);
const Trending: NextPage<{ trending: VideoTrending[] }> = (props) => {
const [selectedCategory, setCategory] = useState<string | undefined>(); const [selectedCategory, setCategory] = useState<string | undefined>();
const [settings] = useSettings(); const [settings] = useSettings();
@ -29,28 +55,12 @@ const Trending: NextPage = () => {
const { isLoading, error, data } = useQuery< const { isLoading, error, data } = useQuery<
VideoTrending[], VideoTrending[],
AxiosError<Error> AxiosError<Error>
>("trendingData", () => >(
axios "trendingData",
.get(`https://${settings.invidiousServer}/api/v1/trending`, { () => fetchTrending(settings.invidiousServer, selectedCategory),
params: { {
fields: [ initialData: props.trending
"title", }
"description",
"descriptionHtml",
"videoId",
"author",
"authorId",
"authorUrl",
"lengthSeconds",
"published",
"publishedText",
"viewCount",
"videoThumbnails"
].join(","),
type: selectedCategory
}
})
.then((res) => res.data)
); );
return ( return (
@ -93,4 +103,14 @@ const Trending: NextPage = () => {
); );
}; };
export const getStaticProps: GetStaticProps = async ({}) => {
const trending = await fetchTrending(
process.env.NEXT_PUBLIC_DEFAULT_SERVER as string
);
return {
props: { trending }
};
};
export default Trending; export default Trending;

@ -25,6 +25,8 @@ export const apiToVideo = (item: VideoTrending | VideoResult): Video => {
views: item.viewCount, views: item.viewCount,
live: item.liveNow, live: item.liveNow,
premium: item.premium, premium: item.premium,
upcoming: item.isUpcoming,
premiereTimestamp: item.premiereTimestamp,
thumbnail: item.videoThumbnails.find( thumbnail: item.videoThumbnails.find(
(thumbnail) => thumbnail.quality == Quality.Maxresdefault (thumbnail) => thumbnail.quality == Quality.Maxresdefault
)?.url as string )?.url as string

Loading…
Cancel
Save