basic trending page

nextui
Guus van Meerveld 2 months ago
parent b6c45a9049
commit 61b54c4081

@ -1,33 +1,36 @@
{
"name": "materialtube",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@nextui-org/react": "^2.2.10",
"framer-motion": "^11.0.12",
"ky": "^1.2.2",
"next": "14.1.3",
"next-pwa": "^5.6.0",
"react": "^18",
"react-dom": "^18",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/next-pwa": "^5.6.9",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10.0.1",
"eslint": "^8",
"eslint-config-next": "14.1.3",
"postcss": "^8",
"tailwindcss": "^3.3.0",
"typescript": "^5"
}
"name": "materialtube",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@nextui-org/react": "^2.2.10",
"@tanstack/react-query": "^5.27.5",
"framer-motion": "^11.0.12",
"ky": "^1.2.2",
"next": "14.1.3",
"next-pwa": "^5.6.0",
"react": "^18",
"react-dom": "^18",
"react-icons": "^5.0.1",
"zod": "^3.22.4"
},
"devDependencies": {
"@tanstack/react-query-devtools": "^5.27.8",
"@types/next-pwa": "^5.6.9",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10.0.1",
"eslint": "^8",
"eslint-config-next": "14.1.3",
"postcss": "^8",
"tailwindcss": "^3.3.0",
"typescript": "^5"
}
}

@ -0,0 +1,73 @@
"use client";
import { Component } from "@/typings/component";
import { useClient } from "@/hooks/useClient";
import { useQuery } from "@tanstack/react-query";
import { Card, CardFooter, CardBody } from "@nextui-org/card";
import { Image } from "@nextui-org/image";
import { Button } from "@nextui-org/button";
import { CircularProgress } from "@nextui-org/progress";
import { Divider } from "@nextui-org/divider";
import Link from "next/link";
import formatNumber from "@/utils/formatNumbers";
export const Trending: Component = ({}) => {
const client = useClient();
const { isLoading, error, data } = useQuery({
queryKey: ["trending"],
queryFn: () => client.getTrending("NL")
});
return (
<div className="container px-4 mx-auto min-h-screen">
{isLoading && !data && (
<div className="flex items-center justify-center h-screen">
<CircularProgress aria-label="Loading trending page..." />
</div>
)}
{data && (
<div className="grid gap-4 py-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
{data.map((video) => (
<Link key={video.id} href={`/watch?v=${video.id}`}>
<Card radius="lg">
<CardBody>
<Image
alt={video.title}
className="object-cover"
height={400}
src={video.thumbnails[0].url}
width={600}
/>
<p className="text-small absolute bottom-3 right-3 bg-content2 p-1">
20:41
</p>
</CardBody>
<Divider />
<CardFooter>
<div className="max-w-full">
<p title={video.title} className="truncate">
{video.title}
</p>
<div className="flex flex-row gap-2 justify-start overflow-scroll">
<p className="text-small tracking-tight text-default-400">
{video.author.name}
</p>
<p className="text-small tracking-tight text-default-400">
{video.uploaded.toLocaleDateString()}
</p>
<p className="text-small tracking-tight text-default-400">
Views: {formatNumber(video.views)}
</p>
</div>
</div>
</CardFooter>
</Card>
</Link>
))}
</div>
)}
</div>
);
};

@ -1,19 +0,0 @@
"use client";
import Client from "@/client";
import { ApiType } from "@/client/adapters";
import { Component } from "@/typings/component";
import { useEffect } from "react";
export const Video: Component = ({}) => {
useEffect(() => {
const client = new Client([
{ baseUrl: "https://invidious.drgns.space", type: ApiType.Invidious },
{ baseUrl: "https://pipedapi.kavin.rocks", type: ApiType.Piped }
]);
client.getTrending("US").then(console.log);
}, []);
return <></>;
};

@ -1,10 +1,9 @@
import { Button } from "@nextui-org/button";
import { Video } from "./Video";
import { Trending } from "./Trending";
export default function Home() {
export default function Page() {
return (
<>
<Video />
<Trending />
</>
);
}

@ -1,7 +1,15 @@
"use client";
import { NextUIProvider } from "@nextui-org/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
export function Providers({ children }: { children: React.ReactNode }) {
return <NextUIProvider>{children}</NextUIProvider>;
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools initialIsOpen={false} />
<NextUIProvider>{children}</NextUIProvider>
</QueryClientProvider>
);
}

@ -0,0 +1,16 @@
import Client from "@/client";
import { ApiType } from "@/client/adapters";
import { useState } from "react";
export const useClient = () => {
const [client] = useState(
() =>
new Client([
// { baseUrl: "https://invidious.drgns.space", type: ApiType.Invidious }
{ baseUrl: "https://pipedapi.kavin.rocks", type: ApiType.Piped }
])
);
return client;
};

@ -0,0 +1,14 @@
const formatNumber = (num: number): string => {
// Nine Zeroes for Billions
return Math.abs(num) >= 1.0e9
? (Math.abs(num) / 1.0e9).toPrecision(3) + "B"
: // Six Zeroes for Millions
Math.abs(num) >= 1.0e6
? (Math.abs(num) / 1.0e6).toPrecision(3) + "M"
: // Three Zeroes for Thousands
Math.abs(num) >= 1.0e3
? (Math.abs(num) / 1.0e3).toPrecision(3) + "K"
: Math.abs(num).toString();
};
export default formatNumber;

@ -2931,6 +2931,30 @@
dependencies:
tslib "^2.4.0"
"@tanstack/query-core@5.27.5":
version "5.27.5"
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.27.5.tgz#de4ede2094d490d0147e943212fcda51f09c0f69"
integrity sha512-HuYOo46NhzNX1SwXCmLf/Skr8B7T56cDHUN+iOhnu7+GOkUMThda64GwZpAqQzBT8TOTBQo6RQaNe0gi3Gi2fw==
"@tanstack/query-devtools@5.27.8":
version "5.27.8"
resolved "https://registry.yarnpkg.com/@tanstack/query-devtools/-/query-devtools-5.27.8.tgz#fa886ae72d0fe9fe5932af12ba0ba3ff484a1224"
integrity sha512-K94gnqvEe6TsDvi8eZYP2JrnQJOIymhVXRR+Xa0xcsryNqG+PeMIDmQQqjwIqbDq36qyUlPAyT6LxXVvVv1Nyw==
"@tanstack/react-query-devtools@^5.27.8":
version "5.27.8"
resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-5.27.8.tgz#eac277afb80e077566ffba85278b8ce4632bac10"
integrity sha512-nWttSF5qhRxyIYh0D9ybZHgAWCOdsBNZf2s0EskYpAxDDrF3lgf/xTzPPzxoX7Z14bxKruVUEpwjQWZg3f/Z7g==
dependencies:
"@tanstack/query-devtools" "5.27.8"
"@tanstack/react-query@^5.27.5":
version "5.27.5"
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.27.5.tgz#2a431c28931bd821d9d96eaf7c6e59b4257ce023"
integrity sha512-VcuQo4CYRGsPsD8/rj9e4WnXN6eU4GKmAs0Yd9a1hLSx6DxAzRaBdrwu6P9lfjpz8bxaYkZRyb5NI+YtLipoYA==
dependencies:
"@tanstack/query-core" "5.27.5"
"@types/estree@0.0.39":
version "0.0.39"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
@ -5537,6 +5561,11 @@ react-dom@^18:
loose-envify "^1.1.0"
scheduler "^0.23.0"
react-icons@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-5.0.1.tgz#1694e11bfa2a2888cab47dcc30154ce90485feee"
integrity sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==
react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"

Loading…
Cancel
Save