From 65b893477310112af500469845f8a6bc17a3ecb0 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Tue, 24 Jun 2025 15:11:30 +0200 Subject: [PATCH] feat: add contributors page to showcase OpenCut contributors and their contributions --- apps/web/src/app/contributors/page.tsx | 277 +++++++++++++++++++++++++ apps/web/src/components/header.tsx | 20 +- apps/web/src/components/icons.tsx | 17 ++ 3 files changed, 303 insertions(+), 11 deletions(-) create mode 100644 apps/web/src/app/contributors/page.tsx diff --git a/apps/web/src/app/contributors/page.tsx b/apps/web/src/app/contributors/page.tsx new file mode 100644 index 0000000..cc8115a --- /dev/null +++ b/apps/web/src/app/contributors/page.tsx @@ -0,0 +1,277 @@ +import { Metadata } from "next"; +import { Header } from "@/components/header"; +import { Badge } from "@/components/ui/badge"; +import { Card, CardContent } from "@/components/ui/card"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Button } from "@/components/ui/button"; +import { ExternalLink } from "lucide-react"; +import Link from "next/link"; +import { GithubIcon } from "@/components/icons"; + +export const metadata: Metadata = { + title: "Contributors - OpenCut", + description: + "Meet the amazing people who contribute to OpenCut, the free and open-source video editor.", + openGraph: { + title: "Contributors - OpenCut", + description: + "Meet the amazing people who contribute to OpenCut, the free and open-source video editor.", + type: "website", + }, +}; + +interface Contributor { + id: number; + login: string; + avatar_url: string; + html_url: string; + contributions: number; + type: string; +} + +async function getContributors(): Promise { + try { + const response = await fetch( + "https://api.github.com/repos/OpenCut-app/OpenCut/contributors", + { + headers: { + Accept: "application/vnd.github.v3+json", + "User-Agent": "OpenCut-Web-App", + }, + next: { revalidate: 600 }, // Cache for 10 minutes + } + ); + + if (!response.ok) { + console.error("Failed to fetch contributors"); + return []; + } + + const contributors = await response.json(); + + // Filter out bots and add additional contributor info if needed + const filteredContributors = contributors.filter( + (contributor: any) => contributor.type === "User" + ); + + return filteredContributors; + } catch (error) { + console.error("Error fetching contributors:", error); + return []; + } +} + +export default async function ContributorsPage() { + const contributors = await getContributors(); + const topContributor = contributors[0]; + const otherContributors = contributors.slice(1); + + return ( +
+
+ +
+ {/* Background decoration */} +
+
+
+
+ +
+
+ {/* Hero Section */} +
+
+ + Open Source +
+

+ Contributors +

+

+ Meet the amazing developers who are building the future of video + editing +

+ + {/* Quick stats */} +
+
+
+ {contributors.length} + contributors +
+
+
+ + {contributors.reduce((sum, c) => sum + c.contributions, 0)} + + contributions +
+
+
+ + {/* Top Contributor Spotlight */} + {topContributor && ( +
+
+

+ Top Contributor +

+

+ Leading the way in contributions +

+
+ + +
+
+ + +
+ + + + {topContributor.login.charAt(0).toUpperCase()} + + +
+ 1 +
+
+

+ {topContributor.login} +

+
+ + {topContributor.contributions} + + contributions +
+
+
+
+ +
+ )} + + {/* Other Contributors */} + {otherContributors.length > 0 && ( +
+
+

+ All Contributors +

+

+ Everyone who makes OpenCut better +

+
+ +
+ {otherContributors.map((contributor, index) => ( + +
+ + + + {contributor.login.charAt(0).toUpperCase()} + + +

+ {contributor.login} +

+

+ {contributor.contributions} +

+
+ + ))} +
+
+ )} + + {contributors.length === 0 && ( +
+
+ +
+

+ No contributors found +

+

+ Unable to load contributors at the moment. Check back later or + view on GitHub. +

+ + + +
+ )} + + {/* CTA Section */} +
+
+

Join the community

+

+ OpenCut is built by developers like you. Every contribution, + no matter how small, helps make video editing more accessible + for everyone. +

+ +
+ + + + + + +
+
+
+
+
+
+
+ ); +} diff --git a/apps/web/src/components/header.tsx b/apps/web/src/components/header.tsx index 8c3ce61..812b0af 100644 --- a/apps/web/src/components/header.tsx +++ b/apps/web/src/components/header.tsx @@ -36,19 +36,17 @@ export function Header() { const rightContent = (