|
|
@@ -0,0 +1,91 @@ |
|
|
import { BlurImage, YouTube } from "@dub/ui"; |
|
|
import { nFormatter } from "@dub/utils"; |
|
|
import { Eye, UserCheck, Video } from "lucide-react"; |
|
|
import { Suspense } from "react"; |
|
|
|
|
|
export function YoutubeChannel({ id }: { id: string }) { |
|
|
return ( |
|
|
<Suspense fallback={<div className="not-prose grid gap-4"></div>}> |
|
|
<YoutubeChannelRSC id={id} /> |
|
|
</Suspense> |
|
|
); |
|
|
} |
|
|
|
|
|
const YoutubeChannelRSC = async ({ id }: { id: string }) => { |
|
|
const res = await fetch( |
|
|
`https://youtube.googleapis.com/youtube/v3/channels?part=statistics&part=brandingSettings&id=${id}&key=${process.env.YOUTUBE_API_KEY}`, |
|
|
).then((res) => res.json()); |
|
|
|
|
|
if (res.error) { |
|
|
return null; |
|
|
} |
|
|
|
|
|
const { statistics, brandingSettings } = res.items[0]; |
|
|
|
|
|
const channel = { |
|
|
subscribers: statistics.subscriberCount, |
|
|
views: statistics.viewCount, |
|
|
videos: statistics.videoCount, |
|
|
title: brandingSettings.channel.title, |
|
|
description: brandingSettings.channel.description, |
|
|
image: brandingSettings.image.bannerExternalUrl, |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<div className="not-prose group mx-auto max-w-md overflow-hidden rounded-xl border border-gray-200 bg-white transition-all hover:border-gray-500 hover:ring-4 hover:ring-gray-200"> |
|
|
<a href={`https://www.youtube.com/channel/${id}`} target="_blank"> |
|
|
<BlurImage |
|
|
src={channel.image} |
|
|
alt={channel.title} |
|
|
width={2560} |
|
|
height={600} |
|
|
className="aspect-[2560/600] w-full object-cover" |
|
|
/> |
|
|
<div> |
|
|
<div className="flex items-start p-4"> |
|
|
<div className="flex-1 space-y-1 pt-1"> |
|
|
<h2 className="text-xl font-semibold tracking-tight"> |
|
|
{channel.title} |
|
|
</h2> |
|
|
<p className="text-sm leading-normal text-gray-500"> |
|
|
{channel.description} |
|
|
</p> |
|
|
</div> |
|
|
<YouTube className="h-8 w-8 text-gray-600 transition-colors group-hover:text-[#ff0000]" /> |
|
|
</div> |
|
|
<div className="flex justify-center space-x-8 border-t border-gray-200 py-4"> |
|
|
<div className="flex items-start space-x-2"> |
|
|
<UserCheck size={16} className="mt-1.5" /> |
|
|
<div> |
|
|
<p className="font-semibold text-gray-600"> |
|
|
{nFormatter(channel.subscribers)} |
|
|
</p> |
|
|
<p className="text-xs font-normal text-gray-500">Subscribers</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div className="flex items-start space-x-2"> |
|
|
<Video size={16} className="mt-1.5" /> |
|
|
<div> |
|
|
<p className="font-semibold text-gray-600"> |
|
|
{nFormatter(channel.videos)} |
|
|
</p> |
|
|
<p className="text-xs font-normal text-gray-500">Videos</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div className="flex items-start space-x-2"> |
|
|
<Eye size={16} className="mt-1.5" /> |
|
|
<div> |
|
|
<p className="font-semibold text-gray-600"> |
|
|
{nFormatter(channel.views)} |
|
|
</p> |
|
|
<p className="text-xs font-normal text-gray-500">Views</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</a> |
|
|
</div> |
|
|
); |
|
|
}; |