added basic search page
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
c7dd2ddd12
commit
5ac329296e
@ -0,0 +1,42 @@
|
||||
import { Component } from "@/typings/component";
|
||||
|
||||
import { ChannelResult as ChannelProps } from "@/client/typings/search";
|
||||
import { Card, CardBody } from "@nextui-org/card";
|
||||
import { Image } from "@nextui-org/image";
|
||||
import Link from "next/link";
|
||||
import NextImage from "next/image";
|
||||
import formatViewCount from "@/utils/formatViewCount";
|
||||
|
||||
export const Channel: Component<{ data: ChannelProps }> = ({ data }) => {
|
||||
const url = `/channel/${data.id}`;
|
||||
|
||||
const imageSize = 200;
|
||||
|
||||
return (
|
||||
<Link href={url}>
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div className="flex flex-row gap-4">
|
||||
<Image
|
||||
width={imageSize}
|
||||
height={imageSize}
|
||||
src={data.thumbnail}
|
||||
alt={data.name}
|
||||
as={NextImage}
|
||||
unoptimized
|
||||
/>
|
||||
|
||||
<div className="flex-1 flex flex-col">
|
||||
<h1 className="text-lg">{data.name}</h1>
|
||||
<div className="flex flex-row gap-4 items-center font-semibold text-default-600">
|
||||
<h1>Subscribers: {formatViewCount(data.subscribers)}</h1>
|
||||
<h1>Videos: {formatViewCount(data.videos)}</h1>
|
||||
</div>
|
||||
<p className="text-default-600">{data.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
};
|
@ -0,0 +1,71 @@
|
||||
"use client";
|
||||
|
||||
import { Search as SearchInput } from "@/components/Search";
|
||||
import { useClient } from "@/hooks/useClient";
|
||||
import { Component } from "@/typings/component";
|
||||
import { Spacer } from "@nextui-org/spacer";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { Channel } from "./Channel";
|
||||
import { Container } from "@/components/Container";
|
||||
import { LoadingPage } from "@/components/LoadingPage";
|
||||
import { Button } from "@nextui-org/button";
|
||||
import { Video } from "./Video";
|
||||
|
||||
export const Search: Component = () => {
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const query = searchParams.get("search_query");
|
||||
|
||||
const client = useClient();
|
||||
|
||||
const { isLoading, error, refetch, data } = useQuery({
|
||||
queryKey: ["search", query],
|
||||
queryFn: () => {
|
||||
if (query === null) return;
|
||||
|
||||
return client.getSearch(query);
|
||||
}
|
||||
});
|
||||
|
||||
const results = data ?? [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container>
|
||||
<SearchInput initialQueryValue={query ?? undefined} />
|
||||
<Spacer y={4} />
|
||||
{isLoading && <LoadingPage />}
|
||||
{error && (
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h1 className="text-xl">
|
||||
An error occurred loading the search page
|
||||
</h1>
|
||||
<h2 className="text-lg">{error.toString()}</h2>
|
||||
<Spacer y={2} />
|
||||
<Button color="primary" onClick={() => refetch()}>
|
||||
Retry
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col gap-4">
|
||||
{results.length != 0 &&
|
||||
results.map((result) => {
|
||||
switch (result.type) {
|
||||
case "channel":
|
||||
return <Channel key={result.id} data={result} />;
|
||||
|
||||
case "video":
|
||||
return <Video key={result.id} data={result} />;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,19 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Search } from "@/components/Search";
|
||||
import { Component } from "@/typings/component";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
|
||||
export const SearchPage: Component = () => {
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const query = searchParams.get("search_query");
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="container mx-auto py-4">
|
||||
<Search initialQueryValue={query || undefined} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,42 @@
|
||||
import { Component } from "@/typings/component";
|
||||
import { VideoResult as VideoProps } from "@/client/typings/search";
|
||||
import { Card, CardBody } from "@nextui-org/card";
|
||||
import { Image } from "@nextui-org/image";
|
||||
|
||||
import NextImage from "next/image";
|
||||
import { useMemo } from "react";
|
||||
import Link from "next/link";
|
||||
|
||||
export const Video: Component<{ data: VideoProps }> = ({ data }) => {
|
||||
const url = `/watch?v=${data.id}`;
|
||||
|
||||
const videoSize = 200;
|
||||
const aspectRatio = 16 / 9;
|
||||
|
||||
const [width, height] = useMemo(() => {
|
||||
return [videoSize * aspectRatio, videoSize];
|
||||
}, [videoSize]);
|
||||
|
||||
return (
|
||||
<Link href={url}>
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div className="flex flex-row gap-4">
|
||||
<Image
|
||||
width={width}
|
||||
height={height}
|
||||
src={data.thumbnail}
|
||||
alt={data.title}
|
||||
as={NextImage}
|
||||
unoptimized
|
||||
className="aspect-video"
|
||||
/>
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-xl">{data.title}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
};
|
@ -0,0 +1,6 @@
|
||||
export interface SearchOptions {
|
||||
page?: number;
|
||||
type?: SearchType;
|
||||
}
|
||||
|
||||
export type SearchType = "video" | "playlist" | "channel" | "all";
|
@ -0,0 +1,21 @@
|
||||
import { Component } from "@/typings/component";
|
||||
import { navHeight } from "./Nav";
|
||||
|
||||
export const Container: Component<{ navbarOffset?: boolean }> = ({
|
||||
children,
|
||||
navbarOffset = true
|
||||
}) => {
|
||||
let height;
|
||||
|
||||
if (navbarOffset) height = `calc(100vh - ${navHeight}px)`;
|
||||
else height = "100vh";
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ minHeight: height }}
|
||||
className="container mx-auto py-4 px-2 flex flex-col"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
Loading…
Reference in new issue