Skip to content

Instantly share code, notes, and snippets.

@thecmdrunner
Forked from steven-tey/youtube-channel.tsx
Created April 9, 2024 20:14
Show Gist options
  • Save thecmdrunner/f15eefcec918ebce5ce14d12de7b2b7f to your computer and use it in GitHub Desktop.
Save thecmdrunner/f15eefcec918ebce5ce14d12de7b2b7f to your computer and use it in GitHub Desktop.

Revisions

  1. @steven-tey steven-tey revised this gist Apr 9, 2024. No changes.
  2. @steven-tey steven-tey created this gist Apr 9, 2024.
    91 changes: 91 additions & 0 deletions youtube-channel.tsx
    Original file line number Diff line number Diff line change
    @@ -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>
    );
    };