@@ -185,10 +123,7 @@ export default function SponsorsSection() {
undefined,
{ year: "numeric", month: "short" },
);
- const sponsorUrl =
- entry.sponsor.websiteUrl ||
- entry.sponsor.linkUrl ||
- `https://github.com/${entry.sponsor.login}`;
+ const sponsorUrl = getSponsorUrl(entry);
return (
- {sponsorUrl
- ?.replace(/^https?:\/\//, "")
- ?.replace(/\/$/, "")}
+ {formatSponsorUrl(sponsorUrl)}
)}
@@ -340,12 +273,10 @@ export default function SponsorsSection() {
>
- {(
+ {formatSponsorUrl(
entry.sponsor.websiteUrl ||
- entry.sponsor.linkUrl
- )
- ?.replace(/^https?:\/\//, "")
- ?.replace(/\/$/, "")}
+ entry.sponsor.linkUrl,
+ )}
)}
@@ -392,10 +323,7 @@ export default function SponsorsSection() {
{ year: "numeric", month: "short" },
);
const wasSpecial = isSpecialSponsor(entry);
- const sponsorUrl =
- entry.sponsor.websiteUrl ||
- entry.sponsor.linkUrl ||
- `https://github.com/${entry.sponsor.login}`;
+ const sponsorUrl = getSponsorUrl(entry);
return (
- {sponsorUrl
- ?.replace(/^https?:\/\//, "")
- ?.replace(/\/$/, "")}
+ {formatSponsorUrl(sponsorUrl)}
)}
diff --git a/apps/web/src/components/special-sponsor-banner.tsx b/apps/web/src/components/special-sponsor-banner.tsx
index c56bf27..191925e 100644
--- a/apps/web/src/components/special-sponsor-banner.tsx
+++ b/apps/web/src/components/special-sponsor-banner.tsx
@@ -1,14 +1,15 @@
"use client";
import Image from "next/image";
import { useEffect, useState } from "react";
+import {
+ filterCurrentSponsors,
+ filterSpecialSponsors,
+ sortSpecialSponsors,
+} from "@/lib/sponsor-utils";
import type { Sponsor } from "@/lib/types";
-const isSpecialSponsor = (sponsor: Sponsor) => {
- return sponsor.monthlyDollars >= 100;
-};
-
export function SpecialSponsorBanner() {
- const [specialSponsor, setSpecialSponsor] = useState
(null);
+ const [specialSponsors, setSpecialSponsors] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
@@ -19,13 +20,11 @@ export function SpecialSponsorBanner() {
})
.then((data) => {
const sponsorsData = Array.isArray(data) ? data : [];
- const specialSponsor = sponsorsData.find(
- (sponsor) => sponsor.monthlyDollars > 0 && isSpecialSponsor(sponsor),
+ const currentSponsors = filterCurrentSponsors(sponsorsData);
+ const specials = sortSpecialSponsors(
+ filterSpecialSponsors(currentSponsors),
);
-
- if (specialSponsor) {
- setSpecialSponsor(specialSponsor);
- }
+ setSpecialSponsors(specials);
setLoading(false);
})
.catch(() => {
@@ -46,29 +45,30 @@ export function SpecialSponsorBanner() {
);
}
- if (!specialSponsor) {
+ if (!specialSponsors.length) {
return null;
}
return (
-
-
-
-
- {specialSponsor.sponsor.name || specialSponsor.sponsor.login}
-
-
+
+ {specialSponsors.map((sponsor) => (
+
+
+
+
+ {sponsor.sponsor.name || sponsor.sponsor.login}
+
+
+
+ ))}
);
diff --git a/apps/web/src/lib/sponsor-utils.ts b/apps/web/src/lib/sponsor-utils.ts
new file mode 100644
index 0000000..2770a11
--- /dev/null
+++ b/apps/web/src/lib/sponsor-utils.ts
@@ -0,0 +1,88 @@
+import type { Sponsor } from "@/lib/types";
+
+export const SPECIAL_SPONSOR_THRESHOLD = 100;
+
+export const getSponsorAmount = (sponsor: Sponsor): number => {
+ // For past sponsors, return 0
+ if (sponsor.monthlyDollars === -1) {
+ return 0;
+ }
+
+ // For one-time sponsors, parse the actual amount from tierName
+ if (sponsor.isOneTime && sponsor.tierName) {
+ const match = sponsor.tierName.match(/\$(\d+(?:\.\d+)?)/);
+ return match ? Number.parseFloat(match[1]) : sponsor.monthlyDollars;
+ }
+
+ // For monthly sponsors, use monthlyDollars
+ return sponsor.monthlyDollars;
+};
+
+export const isSpecialSponsor = (sponsor: Sponsor): boolean => {
+ const amount = getSponsorAmount(sponsor);
+ return amount >= SPECIAL_SPONSOR_THRESHOLD;
+};
+
+export const sortSponsors = (sponsors: Sponsor[]): Sponsor[] => {
+ return sponsors.sort((a, b) => {
+ // Past sponsors (monthlyDollars === -1) go to the end
+ if (a.monthlyDollars === -1 && b.monthlyDollars !== -1) return 1;
+ if (a.monthlyDollars !== -1 && b.monthlyDollars === -1) return -1;
+
+ // If both are past sponsors, sort by creation date (newest first)
+ if (a.monthlyDollars === -1 && b.monthlyDollars === -1) {
+ return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
+ }
+
+ // For current sponsors, sort by actual amount (highest first)
+ const aAmount = getSponsorAmount(a);
+ const bAmount = getSponsorAmount(b);
+ if (aAmount !== bAmount) {
+ return bAmount - aAmount;
+ }
+
+ // If amounts are equal, sort by creation date (oldest first)
+ return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
+ });
+};
+
+export const sortSpecialSponsors = (sponsors: Sponsor[]): Sponsor[] => {
+ return sponsors.sort((a, b) => {
+ // Sort by actual amount (highest first)
+ const aAmount = getSponsorAmount(a);
+ const bAmount = getSponsorAmount(b);
+ if (aAmount !== bAmount) {
+ return bAmount - aAmount;
+ }
+ // If amounts are equal, sort by creation date (oldest first)
+ return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
+ });
+};
+
+export const filterCurrentSponsors = (sponsors: Sponsor[]): Sponsor[] => {
+ return sponsors.filter((sponsor) => sponsor.monthlyDollars !== -1);
+};
+
+export const filterPastSponsors = (sponsors: Sponsor[]): Sponsor[] => {
+ return sponsors.filter((sponsor) => sponsor.monthlyDollars === -1);
+};
+
+export const filterSpecialSponsors = (sponsors: Sponsor[]): Sponsor[] => {
+ return sponsors.filter(isSpecialSponsor);
+};
+
+export const filterRegularSponsors = (sponsors: Sponsor[]): Sponsor[] => {
+ return sponsors.filter((sponsor) => !isSpecialSponsor(sponsor));
+};
+
+export const getSponsorUrl = (sponsor: Sponsor): string => {
+ return (
+ sponsor.sponsor.websiteUrl ||
+ sponsor.sponsor.linkUrl ||
+ `https://github.com/${sponsor.sponsor.login}`
+ );
+};
+
+export const formatSponsorUrl = (url: string): string => {
+ return url?.replace(/^https?:\/\//, "")?.replace(/\/$/, "");
+};